OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2011 The WebRTC Project Authors. All rights reserved. | 2 * Copyright 2011 The WebRTC Project Authors. All rights reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 return false; | 43 return false; |
44 } | 44 } |
45 const uint8_t* u = reinterpret_cast<const uint8_t*>(data); | 45 const uint8_t* u = reinterpret_cast<const uint8_t*>(data); |
46 return len > 17 && u[0] == 22 && u[13] == 1; | 46 return len > 17 && u[0] == 22 && u[13] == 1; |
47 } | 47 } |
48 static bool IsRtpPacket(const char* data, size_t len) { | 48 static bool IsRtpPacket(const char* data, size_t len) { |
49 const uint8_t* u = reinterpret_cast<const uint8_t*>(data); | 49 const uint8_t* u = reinterpret_cast<const uint8_t*>(data); |
50 return (len >= kMinRtpPacketLen && (u[0] & 0xC0) == 0x80); | 50 return (len >= kMinRtpPacketLen && (u[0] & 0xC0) == 0x80); |
51 } | 51 } |
52 | 52 |
53 StreamInterfaceChannel::StreamInterfaceChannel( | 53 StreamInterfaceChannel::StreamInterfaceChannel(IceTransportInternal* channel) |
54 IceTransportInternal* ice_transport) | 54 : channel_(channel), |
55 : ice_transport_(ice_transport), | |
56 state_(rtc::SS_OPEN), | 55 state_(rtc::SS_OPEN), |
57 packets_(kMaxPendingPackets, kMaxDtlsPacketLen) {} | 56 packets_(kMaxPendingPackets, kMaxDtlsPacketLen) {} |
58 | 57 |
59 rtc::StreamResult StreamInterfaceChannel::Read(void* buffer, | 58 rtc::StreamResult StreamInterfaceChannel::Read(void* buffer, |
60 size_t buffer_len, | 59 size_t buffer_len, |
61 size_t* read, | 60 size_t* read, |
62 int* error) { | 61 int* error) { |
63 if (state_ == rtc::SS_CLOSED) | 62 if (state_ == rtc::SS_CLOSED) |
64 return rtc::SR_EOS; | 63 return rtc::SR_EOS; |
65 if (state_ == rtc::SS_OPENING) | 64 if (state_ == rtc::SS_OPENING) |
66 return rtc::SR_BLOCK; | 65 return rtc::SR_BLOCK; |
67 | 66 |
68 if (!packets_.ReadFront(buffer, buffer_len, read)) { | 67 if (!packets_.ReadFront(buffer, buffer_len, read)) { |
69 return rtc::SR_BLOCK; | 68 return rtc::SR_BLOCK; |
70 } | 69 } |
71 | 70 |
72 return rtc::SR_SUCCESS; | 71 return rtc::SR_SUCCESS; |
73 } | 72 } |
74 | 73 |
75 rtc::StreamResult StreamInterfaceChannel::Write(const void* data, | 74 rtc::StreamResult StreamInterfaceChannel::Write(const void* data, |
76 size_t data_len, | 75 size_t data_len, |
77 size_t* written, | 76 size_t* written, |
78 int* error) { | 77 int* error) { |
79 // Always succeeds, since this is an unreliable transport anyway. | 78 // Always succeeds, since this is an unreliable transport anyway. |
80 // TODO(zhihuang): Should this block if ice_transport_'s temporarily | 79 // TODO: Should this block if channel_'s temporarily unwritable? |
81 // unwritable? | |
82 rtc::PacketOptions packet_options; | 80 rtc::PacketOptions packet_options; |
83 ice_transport_->SendPacket(static_cast<const char*>(data), data_len, | 81 channel_->SendPacket(static_cast<const char*>(data), data_len, |
84 packet_options); | 82 packet_options); |
85 if (written) { | 83 if (written) { |
86 *written = data_len; | 84 *written = data_len; |
87 } | 85 } |
88 return rtc::SR_SUCCESS; | 86 return rtc::SR_SUCCESS; |
89 } | 87 } |
90 | 88 |
91 bool StreamInterfaceChannel::OnPacketReceived(const char* data, size_t size) { | 89 bool StreamInterfaceChannel::OnPacketReceived(const char* data, size_t size) { |
92 // We force a read event here to ensure that we don't overflow our queue. | 90 // We force a read event here to ensure that we don't overflow our queue. |
93 bool ret = packets_.WriteBack(data, size, NULL); | 91 bool ret = packets_.WriteBack(data, size, NULL); |
94 RTC_CHECK(ret) << "Failed to write packet to queue."; | 92 RTC_CHECK(ret) << "Failed to write packet to queue."; |
95 if (ret) { | 93 if (ret) { |
96 SignalEvent(this, rtc::SE_READ, 0); | 94 SignalEvent(this, rtc::SE_READ, 0); |
97 } | 95 } |
98 return ret; | 96 return ret; |
99 } | 97 } |
100 | 98 |
101 void StreamInterfaceChannel::Close() { | 99 void StreamInterfaceChannel::Close() { |
102 packets_.Clear(); | 100 packets_.Clear(); |
103 state_ = rtc::SS_CLOSED; | 101 state_ = rtc::SS_CLOSED; |
104 } | 102 } |
105 | 103 |
106 DtlsTransport::DtlsTransport(IceTransportInternal* ice_transport) | 104 DtlsTransportChannelWrapper::DtlsTransportChannelWrapper( |
107 : transport_name_(ice_transport->transport_name()), | 105 IceTransportInternal* channel) |
108 component_(ice_transport->component()), | 106 : TransportChannelImpl(channel->transport_name(), channel->component()), |
109 network_thread_(rtc::Thread::Current()), | 107 network_thread_(rtc::Thread::Current()), |
110 ice_transport_(ice_transport), | 108 channel_(channel), |
111 downward_(NULL), | 109 downward_(NULL), |
112 ssl_role_(rtc::SSL_CLIENT), | 110 ssl_role_(rtc::SSL_CLIENT), |
113 ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_12) { | 111 ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_12) { |
114 ice_transport_->SignalWritableState.connect(this, | 112 channel_->SignalWritableState.connect(this, |
115 &DtlsTransport::OnWritableState); | 113 &DtlsTransportChannelWrapper::OnWritableState); |
116 ice_transport_->SignalReadPacket.connect(this, &DtlsTransport::OnReadPacket); | 114 channel_->SignalReadPacket.connect(this, |
117 ice_transport_->SignalSentPacket.connect(this, &DtlsTransport::OnSentPacket); | 115 &DtlsTransportChannelWrapper::OnReadPacket); |
118 ice_transport_->SignalReadyToSend.connect(this, | 116 channel_->SignalSentPacket.connect( |
119 &DtlsTransport::OnReadyToSend); | 117 this, &DtlsTransportChannelWrapper::OnSentPacket); |
120 ice_transport_->SignalReceivingState.connect( | 118 channel_->SignalReadyToSend.connect(this, |
121 this, &DtlsTransport::OnReceivingState); | 119 &DtlsTransportChannelWrapper::OnReadyToSend); |
| 120 channel_->SignalGatheringState.connect( |
| 121 this, &DtlsTransportChannelWrapper::OnGatheringState); |
| 122 channel_->SignalCandidateGathered.connect( |
| 123 this, &DtlsTransportChannelWrapper::OnCandidateGathered); |
| 124 channel_->SignalCandidatesRemoved.connect( |
| 125 this, &DtlsTransportChannelWrapper::OnCandidatesRemoved); |
| 126 channel_->SignalRoleConflict.connect(this, |
| 127 &DtlsTransportChannelWrapper::OnRoleConflict); |
| 128 channel_->SignalRouteChange.connect(this, |
| 129 &DtlsTransportChannelWrapper::OnRouteChange); |
| 130 channel_->SignalSelectedCandidatePairChanged.connect( |
| 131 this, &DtlsTransportChannelWrapper::OnSelectedCandidatePairChanged); |
| 132 channel_->SignalStateChanged.connect( |
| 133 this, &DtlsTransportChannelWrapper::OnChannelStateChanged); |
| 134 channel_->SignalReceivingState.connect(this, |
| 135 &DtlsTransportChannelWrapper::OnReceivingState); |
122 } | 136 } |
123 | 137 |
124 DtlsTransport::~DtlsTransport() {} | 138 DtlsTransportChannelWrapper::~DtlsTransportChannelWrapper() { |
| 139 } |
125 | 140 |
126 bool DtlsTransport::SetLocalCertificate( | 141 bool DtlsTransportChannelWrapper::SetLocalCertificate( |
127 const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) { | 142 const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) { |
128 if (dtls_active_) { | 143 if (dtls_active_) { |
129 if (certificate == local_certificate_) { | 144 if (certificate == local_certificate_) { |
130 // This may happen during renegotiation. | 145 // This may happen during renegotiation. |
131 LOG_J(LS_INFO, this) << "Ignoring identical DTLS identity"; | 146 LOG_J(LS_INFO, this) << "Ignoring identical DTLS identity"; |
132 return true; | 147 return true; |
133 } else { | 148 } else { |
134 LOG_J(LS_ERROR, this) << "Can't change DTLS local identity in this state"; | 149 LOG_J(LS_ERROR, this) << "Can't change DTLS local identity in this state"; |
135 return false; | 150 return false; |
136 } | 151 } |
137 } | 152 } |
138 | 153 |
139 if (certificate) { | 154 if (certificate) { |
140 local_certificate_ = certificate; | 155 local_certificate_ = certificate; |
141 dtls_active_ = true; | 156 dtls_active_ = true; |
142 } else { | 157 } else { |
143 LOG_J(LS_INFO, this) << "NULL DTLS identity supplied. Not doing DTLS"; | 158 LOG_J(LS_INFO, this) << "NULL DTLS identity supplied. Not doing DTLS"; |
144 } | 159 } |
145 | 160 |
146 return true; | 161 return true; |
147 } | 162 } |
148 | 163 |
149 rtc::scoped_refptr<rtc::RTCCertificate> DtlsTransport::GetLocalCertificate() | 164 rtc::scoped_refptr<rtc::RTCCertificate> |
150 const { | 165 DtlsTransportChannelWrapper::GetLocalCertificate() const { |
151 return local_certificate_; | 166 return local_certificate_; |
152 } | 167 } |
153 | 168 |
154 bool DtlsTransport::SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) { | 169 bool DtlsTransportChannelWrapper::SetSslMaxProtocolVersion( |
| 170 rtc::SSLProtocolVersion version) { |
155 if (dtls_active_) { | 171 if (dtls_active_) { |
156 LOG(LS_ERROR) << "Not changing max. protocol version " | 172 LOG(LS_ERROR) << "Not changing max. protocol version " |
157 << "while DTLS is negotiating"; | 173 << "while DTLS is negotiating"; |
158 return false; | 174 return false; |
159 } | 175 } |
160 | 176 |
161 ssl_max_version_ = version; | 177 ssl_max_version_ = version; |
162 return true; | 178 return true; |
163 } | 179 } |
164 | 180 |
165 bool DtlsTransport::SetSslRole(rtc::SSLRole role) { | 181 bool DtlsTransportChannelWrapper::SetSslRole(rtc::SSLRole role) { |
166 if (dtls_) { | 182 if (dtls_) { |
167 if (ssl_role_ != role) { | 183 if (ssl_role_ != role) { |
168 LOG(LS_ERROR) << "SSL Role can't be reversed after the session is setup."; | 184 LOG(LS_ERROR) << "SSL Role can't be reversed after the session is setup."; |
169 return false; | 185 return false; |
170 } | 186 } |
171 return true; | 187 return true; |
172 } | 188 } |
173 | 189 |
174 ssl_role_ = role; | 190 ssl_role_ = role; |
175 return true; | 191 return true; |
176 } | 192 } |
177 | 193 |
178 bool DtlsTransport::GetSslRole(rtc::SSLRole* role) const { | 194 bool DtlsTransportChannelWrapper::GetSslRole(rtc::SSLRole* role) const { |
179 *role = ssl_role_; | 195 *role = ssl_role_; |
180 return true; | 196 return true; |
181 } | 197 } |
182 | 198 |
183 bool DtlsTransport::GetSslCipherSuite(int* cipher) { | 199 bool DtlsTransportChannelWrapper::GetSslCipherSuite(int* cipher) { |
184 if (dtls_state() != DTLS_TRANSPORT_CONNECTED) { | 200 if (dtls_state() != DTLS_TRANSPORT_CONNECTED) { |
185 return false; | 201 return false; |
186 } | 202 } |
187 | 203 |
188 return dtls_->GetSslCipherSuite(cipher); | 204 return dtls_->GetSslCipherSuite(cipher); |
189 } | 205 } |
190 | 206 |
191 bool DtlsTransport::SetRemoteFingerprint(const std::string& digest_alg, | 207 bool DtlsTransportChannelWrapper::SetRemoteFingerprint( |
192 const uint8_t* digest, | 208 const std::string& digest_alg, |
193 size_t digest_len) { | 209 const uint8_t* digest, |
| 210 size_t digest_len) { |
194 rtc::Buffer remote_fingerprint_value(digest, digest_len); | 211 rtc::Buffer remote_fingerprint_value(digest, digest_len); |
195 | 212 |
196 // Once we have the local certificate, the same remote fingerprint can be set | 213 // Once we have the local certificate, the same remote fingerprint can be set |
197 // multiple times. | 214 // multiple times. |
198 if (dtls_active_ && remote_fingerprint_value_ == remote_fingerprint_value && | 215 if (dtls_active_ && remote_fingerprint_value_ == remote_fingerprint_value && |
199 !digest_alg.empty()) { | 216 !digest_alg.empty()) { |
200 // This may happen during renegotiation. | 217 // This may happen during renegotiation. |
201 LOG_J(LS_INFO, this) << "Ignoring identical remote DTLS fingerprint"; | 218 LOG_J(LS_INFO, this) << "Ignoring identical remote DTLS fingerprint"; |
202 return true; | 219 return true; |
203 } | 220 } |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
251 } | 268 } |
252 | 269 |
253 if (!SetupDtls()) { | 270 if (!SetupDtls()) { |
254 set_dtls_state(DTLS_TRANSPORT_FAILED); | 271 set_dtls_state(DTLS_TRANSPORT_FAILED); |
255 return false; | 272 return false; |
256 } | 273 } |
257 | 274 |
258 return true; | 275 return true; |
259 } | 276 } |
260 | 277 |
261 std::unique_ptr<rtc::SSLCertificate> DtlsTransport::GetRemoteSSLCertificate() | 278 std::unique_ptr<rtc::SSLCertificate> |
262 const { | 279 DtlsTransportChannelWrapper::GetRemoteSSLCertificate() const { |
263 if (!dtls_) { | 280 if (!dtls_) { |
264 return nullptr; | 281 return nullptr; |
265 } | 282 } |
266 | 283 |
267 return dtls_->GetPeerCertificate(); | 284 return dtls_->GetPeerCertificate(); |
268 } | 285 } |
269 | 286 |
270 bool DtlsTransport::SetupDtls() { | 287 bool DtlsTransportChannelWrapper::SetupDtls() { |
271 StreamInterfaceChannel* downward = new StreamInterfaceChannel(ice_transport_); | 288 StreamInterfaceChannel* downward = new StreamInterfaceChannel(channel_); |
272 | 289 |
273 dtls_.reset(rtc::SSLStreamAdapter::Create(downward)); | 290 dtls_.reset(rtc::SSLStreamAdapter::Create(downward)); |
274 if (!dtls_) { | 291 if (!dtls_) { |
275 LOG_J(LS_ERROR, this) << "Failed to create DTLS adapter."; | 292 LOG_J(LS_ERROR, this) << "Failed to create DTLS adapter."; |
276 delete downward; | 293 delete downward; |
277 return false; | 294 return false; |
278 } | 295 } |
279 | 296 |
280 downward_ = downward; | 297 downward_ = downward; |
281 | 298 |
282 dtls_->SetIdentity(local_certificate_->identity()->GetReference()); | 299 dtls_->SetIdentity(local_certificate_->identity()->GetReference()); |
283 dtls_->SetMode(rtc::SSL_MODE_DTLS); | 300 dtls_->SetMode(rtc::SSL_MODE_DTLS); |
284 dtls_->SetMaxProtocolVersion(ssl_max_version_); | 301 dtls_->SetMaxProtocolVersion(ssl_max_version_); |
285 dtls_->SetServerRole(ssl_role_); | 302 dtls_->SetServerRole(ssl_role_); |
286 dtls_->SignalEvent.connect(this, &DtlsTransport::OnDtlsEvent); | 303 dtls_->SignalEvent.connect(this, &DtlsTransportChannelWrapper::OnDtlsEvent); |
287 dtls_->SignalSSLHandshakeError.connect(this, | 304 dtls_->SignalSSLHandshakeError.connect( |
288 &DtlsTransport::OnDtlsHandshakeError); | 305 this, &DtlsTransportChannelWrapper::OnDtlsHandshakeError); |
289 if (remote_fingerprint_value_.size() && | 306 if (remote_fingerprint_value_.size() && |
290 !dtls_->SetPeerCertificateDigest( | 307 !dtls_->SetPeerCertificateDigest( |
291 remote_fingerprint_algorithm_, | 308 remote_fingerprint_algorithm_, |
292 reinterpret_cast<unsigned char*>(remote_fingerprint_value_.data()), | 309 reinterpret_cast<unsigned char*>(remote_fingerprint_value_.data()), |
293 remote_fingerprint_value_.size())) { | 310 remote_fingerprint_value_.size())) { |
294 LOG_J(LS_ERROR, this) << "Couldn't set DTLS certificate digest."; | 311 LOG_J(LS_ERROR, this) << "Couldn't set DTLS certificate digest."; |
295 return false; | 312 return false; |
296 } | 313 } |
297 | 314 |
298 // Set up DTLS-SRTP, if it's been enabled. | 315 // Set up DTLS-SRTP, if it's been enabled. |
299 if (!srtp_ciphers_.empty()) { | 316 if (!srtp_ciphers_.empty()) { |
300 if (!dtls_->SetDtlsSrtpCryptoSuites(srtp_ciphers_)) { | 317 if (!dtls_->SetDtlsSrtpCryptoSuites(srtp_ciphers_)) { |
301 LOG_J(LS_ERROR, this) << "Couldn't set DTLS-SRTP ciphers."; | 318 LOG_J(LS_ERROR, this) << "Couldn't set DTLS-SRTP ciphers."; |
302 return false; | 319 return false; |
303 } | 320 } |
304 } else { | 321 } else { |
305 LOG_J(LS_INFO, this) << "Not using DTLS-SRTP."; | 322 LOG_J(LS_INFO, this) << "Not using DTLS-SRTP."; |
306 } | 323 } |
307 | 324 |
308 LOG_J(LS_INFO, this) << "DTLS setup complete."; | 325 LOG_J(LS_INFO, this) << "DTLS setup complete."; |
309 | 326 |
310 // If the underlying ice_transport is already writable at this point, we may | 327 // If the underlying channel is already writable at this point, we may be |
311 // be able to start DTLS right away. | 328 // able to start DTLS right away. |
312 MaybeStartDtls(); | 329 MaybeStartDtls(); |
313 return true; | 330 return true; |
314 } | 331 } |
315 | 332 |
316 bool DtlsTransport::SetSrtpCryptoSuites(const std::vector<int>& ciphers) { | 333 bool DtlsTransportChannelWrapper::SetSrtpCryptoSuites( |
| 334 const std::vector<int>& ciphers) { |
317 if (srtp_ciphers_ == ciphers) | 335 if (srtp_ciphers_ == ciphers) |
318 return true; | 336 return true; |
319 | 337 |
320 if (dtls_state() == DTLS_TRANSPORT_CONNECTING) { | 338 if (dtls_state() == DTLS_TRANSPORT_CONNECTING) { |
321 LOG(LS_WARNING) << "Ignoring new SRTP ciphers while DTLS is negotiating"; | 339 LOG(LS_WARNING) << "Ignoring new SRTP ciphers while DTLS is negotiating"; |
322 return true; | 340 return true; |
323 } | 341 } |
324 | 342 |
325 if (dtls_state() == DTLS_TRANSPORT_CONNECTED) { | 343 if (dtls_state() == DTLS_TRANSPORT_CONNECTED) { |
326 // We don't support DTLS renegotiation currently. If new set of srtp ciphers | 344 // We don't support DTLS renegotiation currently. If new set of srtp ciphers |
327 // are different than what's being used currently, we will not use it. | 345 // are different than what's being used currently, we will not use it. |
328 // So for now, let's be happy (or sad) with a warning message. | 346 // So for now, let's be happy (or sad) with a warning message. |
329 int current_srtp_cipher; | 347 int current_srtp_cipher; |
330 if (!dtls_->GetDtlsSrtpCryptoSuite(¤t_srtp_cipher)) { | 348 if (!dtls_->GetDtlsSrtpCryptoSuite(¤t_srtp_cipher)) { |
331 LOG(LS_ERROR) | 349 LOG(LS_ERROR) << "Failed to get the current SRTP cipher for DTLS channel"; |
332 << "Failed to get the current SRTP cipher for DTLS transport"; | |
333 return false; | 350 return false; |
334 } | 351 } |
335 const std::vector<int>::const_iterator iter = | 352 const std::vector<int>::const_iterator iter = |
336 std::find(ciphers.begin(), ciphers.end(), current_srtp_cipher); | 353 std::find(ciphers.begin(), ciphers.end(), current_srtp_cipher); |
337 if (iter == ciphers.end()) { | 354 if (iter == ciphers.end()) { |
338 std::string requested_str; | 355 std::string requested_str; |
339 for (size_t i = 0; i < ciphers.size(); ++i) { | 356 for (size_t i = 0; i < ciphers.size(); ++i) { |
340 requested_str.append(" "); | 357 requested_str.append(" "); |
341 requested_str.append(rtc::SrtpCryptoSuiteToName(ciphers[i])); | 358 requested_str.append(rtc::SrtpCryptoSuiteToName(ciphers[i])); |
342 requested_str.append(" "); | 359 requested_str.append(" "); |
343 } | 360 } |
344 LOG(LS_WARNING) << "Ignoring new set of SRTP ciphers, as DTLS " | 361 LOG(LS_WARNING) << "Ignoring new set of SRTP ciphers, as DTLS " |
345 << "renegotiation is not supported currently " | 362 << "renegotiation is not supported currently " |
346 << "current cipher = " << current_srtp_cipher << " and " | 363 << "current cipher = " << current_srtp_cipher << " and " |
347 << "requested = " << "[" << requested_str << "]"; | 364 << "requested = " << "[" << requested_str << "]"; |
348 } | 365 } |
349 return true; | 366 return true; |
350 } | 367 } |
351 | 368 |
352 if (!VERIFY(dtls_state() == DTLS_TRANSPORT_NEW)) { | 369 if (!VERIFY(dtls_state() == DTLS_TRANSPORT_NEW)) { |
353 return false; | 370 return false; |
354 } | 371 } |
355 | 372 |
356 srtp_ciphers_ = ciphers; | 373 srtp_ciphers_ = ciphers; |
357 return true; | 374 return true; |
358 } | 375 } |
359 | 376 |
360 bool DtlsTransport::GetSrtpCryptoSuite(int* cipher) { | 377 bool DtlsTransportChannelWrapper::GetSrtpCryptoSuite(int* cipher) { |
361 if (dtls_state() != DTLS_TRANSPORT_CONNECTED) { | 378 if (dtls_state() != DTLS_TRANSPORT_CONNECTED) { |
362 return false; | 379 return false; |
363 } | 380 } |
364 | 381 |
365 return dtls_->GetDtlsSrtpCryptoSuite(cipher); | 382 return dtls_->GetDtlsSrtpCryptoSuite(cipher); |
366 } | 383 } |
367 | 384 |
368 | 385 |
369 // Called from upper layers to send a media packet. | 386 // Called from upper layers to send a media packet. |
370 int DtlsTransport::SendPacket(const char* data, | 387 int DtlsTransportChannelWrapper::SendPacket( |
371 size_t size, | 388 const char* data, size_t size, |
372 const rtc::PacketOptions& options, | 389 const rtc::PacketOptions& options, int flags) { |
373 int flags) { | |
374 if (!dtls_active_) { | 390 if (!dtls_active_) { |
375 // Not doing DTLS. | 391 // Not doing DTLS. |
376 return ice_transport_->SendPacket(data, size, options); | 392 return channel_->SendPacket(data, size, options); |
377 } | 393 } |
378 | 394 |
379 switch (dtls_state()) { | 395 switch (dtls_state()) { |
380 case DTLS_TRANSPORT_NEW: | 396 case DTLS_TRANSPORT_NEW: |
381 // Can't send data until the connection is active. | 397 // Can't send data until the connection is active. |
382 // TODO(ekr@rtfm.com): assert here if dtls_ is NULL? | 398 // TODO(ekr@rtfm.com): assert here if dtls_ is NULL? |
383 return -1; | 399 return -1; |
384 case DTLS_TRANSPORT_CONNECTING: | 400 case DTLS_TRANSPORT_CONNECTING: |
385 // Can't send data until the connection is active. | 401 // Can't send data until the connection is active. |
386 return -1; | 402 return -1; |
387 case DTLS_TRANSPORT_CONNECTED: | 403 case DTLS_TRANSPORT_CONNECTED: |
388 if (flags & PF_SRTP_BYPASS) { | 404 if (flags & PF_SRTP_BYPASS) { |
389 RTC_DCHECK(!srtp_ciphers_.empty()); | 405 RTC_DCHECK(!srtp_ciphers_.empty()); |
390 if (!IsRtpPacket(data, size)) { | 406 if (!IsRtpPacket(data, size)) { |
391 return -1; | 407 return -1; |
392 } | 408 } |
393 | 409 |
394 return ice_transport_->SendPacket(data, size, options); | 410 return channel_->SendPacket(data, size, options); |
395 } else { | 411 } else { |
396 return (dtls_->WriteAll(data, size, NULL, NULL) == rtc::SR_SUCCESS) | 412 return (dtls_->WriteAll(data, size, NULL, NULL) == rtc::SR_SUCCESS) |
397 ? static_cast<int>(size) | 413 ? static_cast<int>(size) |
398 : -1; | 414 : -1; |
399 } | 415 } |
400 case DTLS_TRANSPORT_FAILED: | 416 case DTLS_TRANSPORT_FAILED: |
401 case DTLS_TRANSPORT_CLOSED: | 417 case DTLS_TRANSPORT_CLOSED: |
402 // Can't send anything when we're closed. | 418 // Can't send anything when we're closed. |
403 return -1; | 419 return -1; |
404 default: | 420 default: |
405 RTC_NOTREACHED(); | 421 RTC_NOTREACHED(); |
406 return -1; | 422 return -1; |
407 } | 423 } |
408 } | 424 } |
409 | 425 |
410 bool DtlsTransport::IsDtlsConnected() { | 426 bool DtlsTransportChannelWrapper::IsDtlsConnected() { |
411 return dtls_ && dtls_->IsTlsConnected(); | 427 return dtls_ && dtls_->IsTlsConnected(); |
412 } | 428 } |
413 | 429 |
414 // The state transition logic here is as follows: | 430 // The state transition logic here is as follows: |
415 // (1) If we're not doing DTLS-SRTP, then the state is just the | 431 // (1) If we're not doing DTLS-SRTP, then the state is just the |
416 // state of the underlying impl() | 432 // state of the underlying impl() |
417 // (2) If we're doing DTLS-SRTP: | 433 // (2) If we're doing DTLS-SRTP: |
418 // - Prior to the DTLS handshake, the state is neither receiving nor | 434 // - Prior to the DTLS handshake, the state is neither receiving nor |
419 // writable | 435 // writable |
420 // - When the impl goes writable for the first time we | 436 // - When the impl goes writable for the first time we |
421 // start the DTLS handshake | 437 // start the DTLS handshake |
422 // - Once the DTLS handshake completes, the state is that of the | 438 // - Once the DTLS handshake completes, the state is that of the |
423 // impl again | 439 // impl again |
424 void DtlsTransport::OnWritableState(rtc::PacketTransportInterface* transport) { | 440 void DtlsTransportChannelWrapper::OnWritableState( |
| 441 rtc::PacketTransportInterface* transport) { |
425 RTC_DCHECK(rtc::Thread::Current() == network_thread_); | 442 RTC_DCHECK(rtc::Thread::Current() == network_thread_); |
426 RTC_DCHECK(transport == ice_transport_); | 443 RTC_DCHECK(transport == channel_); |
427 LOG_J(LS_VERBOSE, this) | 444 LOG_J(LS_VERBOSE, this) |
428 << "DTLSTransportChannelWrapper: ice_transport writable state changed to " | 445 << "DTLSTransportChannelWrapper: channel writable state changed to " |
429 << ice_transport_->writable(); | 446 << channel_->writable(); |
430 | 447 |
431 if (!dtls_active_) { | 448 if (!dtls_active_) { |
432 // Not doing DTLS. | 449 // Not doing DTLS. |
433 // Note: SignalWritableState fired by set_writable. | 450 // Note: SignalWritableState fired by set_writable. |
434 set_writable(ice_transport_->writable()); | 451 set_writable(channel_->writable()); |
435 return; | 452 return; |
436 } | 453 } |
437 | 454 |
438 switch (dtls_state()) { | 455 switch (dtls_state()) { |
439 case DTLS_TRANSPORT_NEW: | 456 case DTLS_TRANSPORT_NEW: |
440 MaybeStartDtls(); | 457 MaybeStartDtls(); |
441 break; | 458 break; |
442 case DTLS_TRANSPORT_CONNECTED: | 459 case DTLS_TRANSPORT_CONNECTED: |
443 // Note: SignalWritableState fired by set_writable. | 460 // Note: SignalWritableState fired by set_writable. |
444 set_writable(ice_transport_->writable()); | 461 set_writable(channel_->writable()); |
445 break; | 462 break; |
446 case DTLS_TRANSPORT_CONNECTING: | 463 case DTLS_TRANSPORT_CONNECTING: |
447 // Do nothing. | 464 // Do nothing. |
448 break; | 465 break; |
449 case DTLS_TRANSPORT_FAILED: | 466 case DTLS_TRANSPORT_FAILED: |
450 case DTLS_TRANSPORT_CLOSED: | 467 case DTLS_TRANSPORT_CLOSED: |
451 // Should not happen. Do nothing. | 468 // Should not happen. Do nothing. |
452 break; | 469 break; |
453 } | 470 } |
454 } | 471 } |
455 | 472 |
456 void DtlsTransport::OnReceivingState(rtc::PacketTransportInterface* transport) { | 473 void DtlsTransportChannelWrapper::OnReceivingState( |
| 474 rtc::PacketTransportInterface* transport) { |
457 RTC_DCHECK(rtc::Thread::Current() == network_thread_); | 475 RTC_DCHECK(rtc::Thread::Current() == network_thread_); |
458 RTC_DCHECK(transport == ice_transport_); | 476 RTC_DCHECK(transport == channel_); |
459 LOG_J(LS_VERBOSE, this) << "DTLSTransportChannelWrapper: ice_transport " | 477 LOG_J(LS_VERBOSE, this) |
460 "receiving state changed to " | 478 << "DTLSTransportChannelWrapper: channel receiving state changed to " |
461 << ice_transport_->receiving(); | 479 << channel_->receiving(); |
462 if (!dtls_active_ || dtls_state() == DTLS_TRANSPORT_CONNECTED) { | 480 if (!dtls_active_ || dtls_state() == DTLS_TRANSPORT_CONNECTED) { |
463 // Note: SignalReceivingState fired by set_receiving. | 481 // Note: SignalReceivingState fired by set_receiving. |
464 set_receiving(ice_transport_->receiving()); | 482 set_receiving(channel_->receiving()); |
465 } | 483 } |
466 } | 484 } |
467 | 485 |
468 void DtlsTransport::OnReadPacket(rtc::PacketTransportInterface* transport, | 486 void DtlsTransportChannelWrapper::OnReadPacket( |
469 const char* data, | 487 rtc::PacketTransportInterface* transport, |
470 size_t size, | 488 const char* data, |
471 const rtc::PacketTime& packet_time, | 489 size_t size, |
472 int flags) { | 490 const rtc::PacketTime& packet_time, |
| 491 int flags) { |
473 RTC_DCHECK(rtc::Thread::Current() == network_thread_); | 492 RTC_DCHECK(rtc::Thread::Current() == network_thread_); |
474 RTC_DCHECK(transport == ice_transport_); | 493 RTC_DCHECK(transport == channel_); |
475 RTC_DCHECK(flags == 0); | 494 RTC_DCHECK(flags == 0); |
476 | 495 |
477 if (!dtls_active_) { | 496 if (!dtls_active_) { |
478 // Not doing DTLS. | 497 // Not doing DTLS. |
479 SignalReadPacket(this, data, size, packet_time, 0); | 498 SignalReadPacket(this, data, size, packet_time, 0); |
480 return; | 499 return; |
481 } | 500 } |
482 | 501 |
483 switch (dtls_state()) { | 502 switch (dtls_state()) { |
484 case DTLS_TRANSPORT_NEW: | 503 case DTLS_TRANSPORT_NEW: |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
536 SignalReadPacket(this, data, size, packet_time, PF_SRTP_BYPASS); | 555 SignalReadPacket(this, data, size, packet_time, PF_SRTP_BYPASS); |
537 } | 556 } |
538 break; | 557 break; |
539 case DTLS_TRANSPORT_FAILED: | 558 case DTLS_TRANSPORT_FAILED: |
540 case DTLS_TRANSPORT_CLOSED: | 559 case DTLS_TRANSPORT_CLOSED: |
541 // This shouldn't be happening. Drop the packet. | 560 // This shouldn't be happening. Drop the packet. |
542 break; | 561 break; |
543 } | 562 } |
544 } | 563 } |
545 | 564 |
546 void DtlsTransport::OnSentPacket(rtc::PacketTransportInterface* transport, | 565 void DtlsTransportChannelWrapper::OnSentPacket( |
547 const rtc::SentPacket& sent_packet) { | 566 rtc::PacketTransportInterface* transport, |
| 567 const rtc::SentPacket& sent_packet) { |
548 RTC_DCHECK(rtc::Thread::Current() == network_thread_); | 568 RTC_DCHECK(rtc::Thread::Current() == network_thread_); |
549 | 569 |
550 SignalSentPacket(this, sent_packet); | 570 SignalSentPacket(this, sent_packet); |
551 } | 571 } |
552 | 572 |
553 void DtlsTransport::OnReadyToSend(rtc::PacketTransportInterface* transport) { | 573 void DtlsTransportChannelWrapper::OnReadyToSend( |
| 574 rtc::PacketTransportInterface* transport) { |
554 if (writable()) { | 575 if (writable()) { |
555 SignalReadyToSend(this); | 576 SignalReadyToSend(this); |
556 } | 577 } |
557 } | 578 } |
558 | 579 |
559 void DtlsTransport::OnDtlsEvent(rtc::StreamInterface* dtls, int sig, int err) { | 580 void DtlsTransportChannelWrapper::OnDtlsEvent(rtc::StreamInterface* dtls, |
| 581 int sig, int err) { |
560 RTC_DCHECK(rtc::Thread::Current() == network_thread_); | 582 RTC_DCHECK(rtc::Thread::Current() == network_thread_); |
561 RTC_DCHECK(dtls == dtls_.get()); | 583 RTC_DCHECK(dtls == dtls_.get()); |
562 if (sig & rtc::SE_OPEN) { | 584 if (sig & rtc::SE_OPEN) { |
563 // This is the first time. | 585 // This is the first time. |
564 LOG_J(LS_INFO, this) << "DTLS handshake complete."; | 586 LOG_J(LS_INFO, this) << "DTLS handshake complete."; |
565 if (dtls_->GetState() == rtc::SS_OPEN) { | 587 if (dtls_->GetState() == rtc::SS_OPEN) { |
566 // The check for OPEN shouldn't be necessary but let's make | 588 // The check for OPEN shouldn't be necessary but let's make |
567 // sure we don't accidentally frob the state if it's closed. | 589 // sure we don't accidentally frob the state if it's closed. |
568 set_dtls_state(DTLS_TRANSPORT_CONNECTED); | 590 set_dtls_state(DTLS_TRANSPORT_CONNECTED); |
569 set_writable(true); | 591 set_writable(true); |
570 } | 592 } |
571 } | 593 } |
572 if (sig & rtc::SE_READ) { | 594 if (sig & rtc::SE_READ) { |
573 char buf[kMaxDtlsPacketLen]; | 595 char buf[kMaxDtlsPacketLen]; |
574 size_t read; | 596 size_t read; |
575 int read_error; | 597 int read_error; |
576 rtc::StreamResult ret = dtls_->Read(buf, sizeof(buf), &read, &read_error); | 598 rtc::StreamResult ret = dtls_->Read(buf, sizeof(buf), &read, &read_error); |
577 if (ret == rtc::SR_SUCCESS) { | 599 if (ret == rtc::SR_SUCCESS) { |
578 SignalReadPacket(this, buf, read, rtc::CreatePacketTime(0), 0); | 600 SignalReadPacket(this, buf, read, rtc::CreatePacketTime(0), 0); |
579 } else if (ret == rtc::SR_EOS) { | 601 } else if (ret == rtc::SR_EOS) { |
580 // Remote peer shut down the association with no error. | 602 // Remote peer shut down the association with no error. |
581 LOG_J(LS_INFO, this) << "DTLS transport closed"; | 603 LOG_J(LS_INFO, this) << "DTLS channel closed"; |
582 set_writable(false); | 604 set_writable(false); |
583 set_dtls_state(DTLS_TRANSPORT_CLOSED); | 605 set_dtls_state(DTLS_TRANSPORT_CLOSED); |
584 } else if (ret == rtc::SR_ERROR) { | 606 } else if (ret == rtc::SR_ERROR) { |
585 // Remote peer shut down the association with an error. | 607 // Remote peer shut down the association with an error. |
586 LOG_J(LS_INFO, this) << "DTLS transport error, code=" << read_error; | 608 LOG_J(LS_INFO, this) << "DTLS channel error, code=" << read_error; |
587 set_writable(false); | 609 set_writable(false); |
588 set_dtls_state(DTLS_TRANSPORT_FAILED); | 610 set_dtls_state(DTLS_TRANSPORT_FAILED); |
589 } | 611 } |
590 } | 612 } |
591 if (sig & rtc::SE_CLOSE) { | 613 if (sig & rtc::SE_CLOSE) { |
592 RTC_DCHECK(sig == rtc::SE_CLOSE); // SE_CLOSE should be by itself. | 614 RTC_DCHECK(sig == rtc::SE_CLOSE); // SE_CLOSE should be by itself. |
593 set_writable(false); | 615 set_writable(false); |
594 if (!err) { | 616 if (!err) { |
595 LOG_J(LS_INFO, this) << "DTLS transport closed"; | 617 LOG_J(LS_INFO, this) << "DTLS channel closed"; |
596 set_dtls_state(DTLS_TRANSPORT_CLOSED); | 618 set_dtls_state(DTLS_TRANSPORT_CLOSED); |
597 } else { | 619 } else { |
598 LOG_J(LS_INFO, this) << "DTLS transport error, code=" << err; | 620 LOG_J(LS_INFO, this) << "DTLS channel error, code=" << err; |
599 set_dtls_state(DTLS_TRANSPORT_FAILED); | 621 set_dtls_state(DTLS_TRANSPORT_FAILED); |
600 } | 622 } |
601 } | 623 } |
602 } | 624 } |
603 | 625 |
604 void DtlsTransport::MaybeStartDtls() { | 626 void DtlsTransportChannelWrapper::MaybeStartDtls() { |
605 if (dtls_ && ice_transport_->writable()) { | 627 if (dtls_ && channel_->writable()) { |
606 if (dtls_->StartSSL()) { | 628 if (dtls_->StartSSL()) { |
607 // This should never fail: | 629 // This should never fail: |
608 // Because we are operating in a nonblocking mode and all | 630 // Because we are operating in a nonblocking mode and all |
609 // incoming packets come in via OnReadPacket(), which rejects | 631 // incoming packets come in via OnReadPacket(), which rejects |
610 // packets in this state, the incoming queue must be empty. We | 632 // packets in this state, the incoming queue must be empty. We |
611 // ignore write errors, thus any errors must be because of | 633 // ignore write errors, thus any errors must be because of |
612 // configuration and therefore are our fault. | 634 // configuration and therefore are our fault. |
613 RTC_NOTREACHED() << "StartSSL failed."; | 635 RTC_NOTREACHED() << "StartSSL failed."; |
614 LOG_J(LS_ERROR, this) << "Couldn't start DTLS handshake"; | 636 LOG_J(LS_ERROR, this) << "Couldn't start DTLS handshake"; |
615 set_dtls_state(DTLS_TRANSPORT_FAILED); | 637 set_dtls_state(DTLS_TRANSPORT_FAILED); |
616 return; | 638 return; |
617 } | 639 } |
618 LOG_J(LS_INFO, this) << "DtlsTransport: Started DTLS handshake"; | 640 LOG_J(LS_INFO, this) |
| 641 << "DtlsTransportChannelWrapper: Started DTLS handshake"; |
619 set_dtls_state(DTLS_TRANSPORT_CONNECTING); | 642 set_dtls_state(DTLS_TRANSPORT_CONNECTING); |
620 // Now that the handshake has started, we can process a cached ClientHello | 643 // Now that the handshake has started, we can process a cached ClientHello |
621 // (if one exists). | 644 // (if one exists). |
622 if (cached_client_hello_.size()) { | 645 if (cached_client_hello_.size()) { |
623 if (ssl_role_ == rtc::SSL_SERVER) { | 646 if (ssl_role_ == rtc::SSL_SERVER) { |
624 LOG_J(LS_INFO, this) << "Handling cached DTLS ClientHello packet."; | 647 LOG_J(LS_INFO, this) << "Handling cached DTLS ClientHello packet."; |
625 if (!HandleDtlsPacket(cached_client_hello_.data<char>(), | 648 if (!HandleDtlsPacket(cached_client_hello_.data<char>(), |
626 cached_client_hello_.size())) { | 649 cached_client_hello_.size())) { |
627 LOG_J(LS_ERROR, this) << "Failed to handle DTLS packet."; | 650 LOG_J(LS_ERROR, this) << "Failed to handle DTLS packet."; |
628 } | 651 } |
629 } else { | 652 } else { |
630 LOG_J(LS_WARNING, this) << "Discarding cached DTLS ClientHello packet " | 653 LOG_J(LS_WARNING, this) << "Discarding cached DTLS ClientHello packet " |
631 << "because we don't have the server role."; | 654 << "because we don't have the server role."; |
632 } | 655 } |
633 cached_client_hello_.Clear(); | 656 cached_client_hello_.Clear(); |
634 } | 657 } |
635 } | 658 } |
636 } | 659 } |
637 | 660 |
638 // Called from OnReadPacket when a DTLS packet is received. | 661 // Called from OnReadPacket when a DTLS packet is received. |
639 bool DtlsTransport::HandleDtlsPacket(const char* data, size_t size) { | 662 bool DtlsTransportChannelWrapper::HandleDtlsPacket(const char* data, |
| 663 size_t size) { |
640 // Sanity check we're not passing junk that | 664 // Sanity check we're not passing junk that |
641 // just looks like DTLS. | 665 // just looks like DTLS. |
642 const uint8_t* tmp_data = reinterpret_cast<const uint8_t*>(data); | 666 const uint8_t* tmp_data = reinterpret_cast<const uint8_t*>(data); |
643 size_t tmp_size = size; | 667 size_t tmp_size = size; |
644 while (tmp_size > 0) { | 668 while (tmp_size > 0) { |
645 if (tmp_size < kDtlsRecordHeaderLen) | 669 if (tmp_size < kDtlsRecordHeaderLen) |
646 return false; // Too short for the header | 670 return false; // Too short for the header |
647 | 671 |
648 size_t record_len = (tmp_data[11] << 8) | (tmp_data[12]); | 672 size_t record_len = (tmp_data[11] << 8) | (tmp_data[12]); |
649 if ((record_len + kDtlsRecordHeaderLen) > tmp_size) | 673 if ((record_len + kDtlsRecordHeaderLen) > tmp_size) |
650 return false; // Body too short | 674 return false; // Body too short |
651 | 675 |
652 tmp_data += record_len + kDtlsRecordHeaderLen; | 676 tmp_data += record_len + kDtlsRecordHeaderLen; |
653 tmp_size -= record_len + kDtlsRecordHeaderLen; | 677 tmp_size -= record_len + kDtlsRecordHeaderLen; |
654 } | 678 } |
655 | 679 |
656 // Looks good. Pass to the SIC which ends up being passed to | 680 // Looks good. Pass to the SIC which ends up being passed to |
657 // the DTLS stack. | 681 // the DTLS stack. |
658 return downward_->OnPacketReceived(data, size); | 682 return downward_->OnPacketReceived(data, size); |
659 } | 683 } |
660 | 684 |
661 void DtlsTransport::set_receiving(bool receiving) { | 685 void DtlsTransportChannelWrapper::OnGatheringState( |
662 if (receiving_ == receiving) { | 686 IceTransportInternal* channel) { |
663 return; | 687 RTC_DCHECK(channel == channel_); |
664 } | 688 SignalGatheringState(this); |
665 receiving_ = receiving; | |
666 SignalReceivingState(this); | |
667 } | 689 } |
668 | 690 |
669 void DtlsTransport::set_writable(bool writable) { | 691 void DtlsTransportChannelWrapper::OnCandidateGathered( |
670 if (writable_ == writable) { | 692 IceTransportInternal* channel, |
671 return; | 693 const Candidate& c) { |
672 } | 694 RTC_DCHECK(channel == channel_); |
673 LOG_J(LS_VERBOSE, this) << "set_writable from:" << writable_ << " to " | 695 SignalCandidateGathered(this, c); |
674 << writable; | |
675 writable_ = writable; | |
676 if (writable_) { | |
677 SignalReadyToSend(this); | |
678 } | |
679 SignalWritableState(this); | |
680 } | 696 } |
681 | 697 |
682 void DtlsTransport::set_dtls_state(DtlsTransportState state) { | 698 void DtlsTransportChannelWrapper::OnCandidatesRemoved( |
683 if (dtls_state_ == state) { | 699 IceTransportInternal* channel, |
684 return; | 700 const Candidates& candidates) { |
685 } | 701 RTC_DCHECK(channel == channel_); |
686 LOG_J(LS_VERBOSE, this) << "set_dtls_state from:" << dtls_state_ << " to " | 702 SignalCandidatesRemoved(this, candidates); |
687 << state; | |
688 dtls_state_ = state; | |
689 SignalDtlsState(this, state); | |
690 } | 703 } |
691 | 704 |
692 void DtlsTransport::OnDtlsHandshakeError(rtc::SSLHandshakeError error) { | 705 void DtlsTransportChannelWrapper::OnRoleConflict( |
| 706 IceTransportInternal* channel) { |
| 707 RTC_DCHECK(channel == channel_); |
| 708 SignalRoleConflict(this); |
| 709 } |
| 710 |
| 711 void DtlsTransportChannelWrapper::OnRouteChange(IceTransportInternal* channel, |
| 712 const Candidate& candidate) { |
| 713 RTC_DCHECK(channel == channel_); |
| 714 SignalRouteChange(this, candidate); |
| 715 } |
| 716 |
| 717 void DtlsTransportChannelWrapper::OnSelectedCandidatePairChanged( |
| 718 IceTransportInternal* channel, |
| 719 CandidatePairInterface* selected_candidate_pair, |
| 720 int last_sent_packet_id, |
| 721 bool ready_to_send) { |
| 722 RTC_DCHECK(channel == channel_); |
| 723 SignalSelectedCandidatePairChanged(this, selected_candidate_pair, |
| 724 last_sent_packet_id, ready_to_send); |
| 725 } |
| 726 |
| 727 void DtlsTransportChannelWrapper::OnChannelStateChanged( |
| 728 IceTransportInternal* channel) { |
| 729 RTC_DCHECK(channel == channel_); |
| 730 SignalStateChanged(this); |
| 731 } |
| 732 |
| 733 void DtlsTransportChannelWrapper::OnDtlsHandshakeError( |
| 734 rtc::SSLHandshakeError error) { |
693 SignalDtlsHandshakeError(error); | 735 SignalDtlsHandshakeError(error); |
694 } | 736 } |
695 | 737 |
696 } // namespace cricket | 738 } // namespace cricket |
OLD | NEW |