blob: 8994aa895a6e1ddf770a3beec3f28410adb4ab4e [file] [log] [blame]
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +00001/*
2 * Copyright 2012 The WebRTC Project Authors. All rights reserved.
3 *
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
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/p2p/base/turnport.h"
12
13#include <functional>
14
15#include "webrtc/p2p/base/common.h"
16#include "webrtc/p2p/base/stun.h"
17#include "webrtc/base/asyncpacketsocket.h"
18#include "webrtc/base/byteorder.h"
19#include "webrtc/base/common.h"
20#include "webrtc/base/logging.h"
21#include "webrtc/base/nethelpers.h"
22#include "webrtc/base/socketaddress.h"
23#include "webrtc/base/stringencode.h"
24
25namespace cricket {
26
27// TODO(juberti): Move to stun.h when relay messages have been renamed.
28static const int TURN_ALLOCATE_REQUEST = STUN_ALLOCATE_REQUEST;
29
30// TODO(juberti): Extract to turnmessage.h
31static const int TURN_DEFAULT_PORT = 3478;
32static const int TURN_CHANNEL_NUMBER_START = 0x4000;
33static const int TURN_PERMISSION_TIMEOUT = 5 * 60 * 1000; // 5 minutes
34
35static const size_t TURN_CHANNEL_HEADER_SIZE = 4U;
36
37// Retry at most twice (i.e. three different ALLOCATE requests) on
38// STUN_ERROR_ALLOCATION_MISMATCH error per rfc5766.
39static const size_t MAX_ALLOCATE_MISMATCH_RETRIES = 2;
40
41inline bool IsTurnChannelData(uint16 msg_type) {
42 return ((msg_type & 0xC000) == 0x4000); // MSB are 0b01
43}
44
45static int GetRelayPreference(cricket::ProtocolType proto, bool secure) {
46 int relay_preference = ICE_TYPE_PREFERENCE_RELAY;
47 if (proto == cricket::PROTO_TCP) {
48 relay_preference -= 1;
49 if (secure)
50 relay_preference -= 1;
51 }
52
53 ASSERT(relay_preference >= 0);
54 return relay_preference;
55}
56
57class TurnAllocateRequest : public StunRequest {
58 public:
59 explicit TurnAllocateRequest(TurnPort* port);
60 virtual void Prepare(StunMessage* request);
61 virtual void OnResponse(StunMessage* response);
62 virtual void OnErrorResponse(StunMessage* response);
63 virtual void OnTimeout();
64
65 private:
66 // Handles authentication challenge from the server.
67 void OnAuthChallenge(StunMessage* response, int code);
68 void OnTryAlternate(StunMessage* response, int code);
69 void OnUnknownAttribute(StunMessage* response);
70
71 TurnPort* port_;
72};
73
74class TurnRefreshRequest : public StunRequest {
75 public:
76 explicit TurnRefreshRequest(TurnPort* port);
77 virtual void Prepare(StunMessage* request);
78 virtual void OnResponse(StunMessage* response);
79 virtual void OnErrorResponse(StunMessage* response);
80 virtual void OnTimeout();
81
82 private:
83 TurnPort* port_;
84};
85
86class TurnCreatePermissionRequest : public StunRequest,
87 public sigslot::has_slots<> {
88 public:
89 TurnCreatePermissionRequest(TurnPort* port, TurnEntry* entry,
90 const rtc::SocketAddress& ext_addr);
91 virtual void Prepare(StunMessage* request);
92 virtual void OnResponse(StunMessage* response);
93 virtual void OnErrorResponse(StunMessage* response);
94 virtual void OnTimeout();
95
96 private:
97 void OnEntryDestroyed(TurnEntry* entry);
98
99 TurnPort* port_;
100 TurnEntry* entry_;
101 rtc::SocketAddress ext_addr_;
102};
103
104class TurnChannelBindRequest : public StunRequest,
105 public sigslot::has_slots<> {
106 public:
107 TurnChannelBindRequest(TurnPort* port, TurnEntry* entry, int channel_id,
108 const rtc::SocketAddress& ext_addr);
109 virtual void Prepare(StunMessage* request);
110 virtual void OnResponse(StunMessage* response);
111 virtual void OnErrorResponse(StunMessage* response);
112 virtual void OnTimeout();
113
114 private:
115 void OnEntryDestroyed(TurnEntry* entry);
116
117 TurnPort* port_;
118 TurnEntry* entry_;
119 int channel_id_;
120 rtc::SocketAddress ext_addr_;
121};
122
123// Manages a "connection" to a remote destination. We will attempt to bring up
124// a channel for this remote destination to reduce the overhead of sending data.
125class TurnEntry : public sigslot::has_slots<> {
126 public:
127 enum BindState { STATE_UNBOUND, STATE_BINDING, STATE_BOUND };
128 TurnEntry(TurnPort* port, int channel_id,
129 const rtc::SocketAddress& ext_addr);
130
131 TurnPort* port() { return port_; }
132
133 int channel_id() const { return channel_id_; }
134 const rtc::SocketAddress& address() const { return ext_addr_; }
135 BindState state() const { return state_; }
136
137 // Helper methods to send permission and channel bind requests.
138 void SendCreatePermissionRequest();
139 void SendChannelBindRequest(int delay);
140 // Sends a packet to the given destination address.
141 // This will wrap the packet in STUN if necessary.
142 int Send(const void* data, size_t size, bool payload,
143 const rtc::PacketOptions& options);
144
145 void OnCreatePermissionSuccess();
146 void OnCreatePermissionError(StunMessage* response, int code);
147 void OnChannelBindSuccess();
148 void OnChannelBindError(StunMessage* response, int code);
149 // Signal sent when TurnEntry is destroyed.
150 sigslot::signal1<TurnEntry*> SignalDestroyed;
151
152 private:
153 TurnPort* port_;
154 int channel_id_;
155 rtc::SocketAddress ext_addr_;
156 BindState state_;
157};
158
159TurnPort::TurnPort(rtc::Thread* thread,
160 rtc::PacketSocketFactory* factory,
161 rtc::Network* network,
162 rtc::AsyncPacketSocket* socket,
163 const std::string& username,
164 const std::string& password,
165 const ProtocolAddress& server_address,
166 const RelayCredentials& credentials,
167 int server_priority)
168 : Port(thread, factory, network, socket->GetLocalAddress().ipaddr(),
169 username, password),
170 server_address_(server_address),
171 credentials_(credentials),
172 socket_(socket),
173 resolver_(NULL),
174 error_(0),
175 request_manager_(thread),
176 next_channel_number_(TURN_CHANNEL_NUMBER_START),
177 connected_(false),
178 server_priority_(server_priority),
179 allocate_mismatch_retries_(0) {
180 request_manager_.SignalSendPacket.connect(this, &TurnPort::OnSendStunPacket);
181}
182
183TurnPort::TurnPort(rtc::Thread* thread,
184 rtc::PacketSocketFactory* factory,
185 rtc::Network* network,
186 const rtc::IPAddress& ip,
pkasting@chromium.org332331f2014-11-06 20:19:22 +0000187 uint16 min_port,
188 uint16 max_port,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000189 const std::string& username,
190 const std::string& password,
191 const ProtocolAddress& server_address,
192 const RelayCredentials& credentials,
193 int server_priority)
194 : Port(thread, RELAY_PORT_TYPE, factory, network, ip, min_port, max_port,
195 username, password),
196 server_address_(server_address),
197 credentials_(credentials),
198 socket_(NULL),
199 resolver_(NULL),
200 error_(0),
201 request_manager_(thread),
202 next_channel_number_(TURN_CHANNEL_NUMBER_START),
203 connected_(false),
204 server_priority_(server_priority),
205 allocate_mismatch_retries_(0) {
206 request_manager_.SignalSendPacket.connect(this, &TurnPort::OnSendStunPacket);
207}
208
209TurnPort::~TurnPort() {
210 // TODO(juberti): Should this even be necessary?
211 while (!entries_.empty()) {
212 DestroyEntry(entries_.front()->address());
213 }
214 if (resolver_) {
215 resolver_->Destroy(false);
216 }
217 if (!SharedSocket()) {
218 delete socket_;
219 }
220}
221
222void TurnPort::PrepareAddress() {
223 if (credentials_.username.empty() ||
224 credentials_.password.empty()) {
225 LOG(LS_ERROR) << "Allocation can't be started without setting the"
226 << " TURN server credentials for the user.";
227 OnAllocateError();
228 return;
229 }
230
231 if (!server_address_.address.port()) {
232 // We will set default TURN port, if no port is set in the address.
233 server_address_.address.SetPort(TURN_DEFAULT_PORT);
234 }
235
236 if (server_address_.address.IsUnresolved()) {
237 ResolveTurnAddress(server_address_.address);
238 } else {
239 // If protocol family of server address doesn't match with local, return.
240 if (!IsCompatibleAddress(server_address_.address)) {
241 LOG(LS_ERROR) << "Server IP address family does not match with "
242 << "local host address family type";
243 OnAllocateError();
244 return;
245 }
246
247 // Insert the current address to prevent redirection pingpong.
248 attempted_server_addresses_.insert(server_address_.address);
249
250 LOG_J(LS_INFO, this) << "Trying to connect to TURN server via "
251 << ProtoToString(server_address_.proto) << " @ "
252 << server_address_.address.ToSensitiveString();
253 if (!CreateTurnClientSocket()) {
254 OnAllocateError();
255 } else if (server_address_.proto == PROTO_UDP) {
256 // If its UDP, send AllocateRequest now.
257 // For TCP and TLS AllcateRequest will be sent by OnSocketConnect.
258 SendRequest(new TurnAllocateRequest(this), 0);
259 }
260 }
261}
262
263bool TurnPort::CreateTurnClientSocket() {
264 ASSERT(!socket_ || SharedSocket());
265
266 if (server_address_.proto == PROTO_UDP && !SharedSocket()) {
267 socket_ = socket_factory()->CreateUdpSocket(
268 rtc::SocketAddress(ip(), 0), min_port(), max_port());
269 } else if (server_address_.proto == PROTO_TCP) {
270 ASSERT(!SharedSocket());
271 int opts = rtc::PacketSocketFactory::OPT_STUN;
272 // If secure bit is enabled in server address, use TLS over TCP.
273 if (server_address_.secure) {
274 opts |= rtc::PacketSocketFactory::OPT_TLS;
275 }
276 socket_ = socket_factory()->CreateClientTcpSocket(
277 rtc::SocketAddress(ip(), 0), server_address_.address,
278 proxy(), user_agent(), opts);
279 }
280
281 if (!socket_) {
282 error_ = SOCKET_ERROR;
283 return false;
284 }
285
286 // Apply options if any.
287 for (SocketOptionsMap::iterator iter = socket_options_.begin();
288 iter != socket_options_.end(); ++iter) {
289 socket_->SetOption(iter->first, iter->second);
290 }
291
292 if (!SharedSocket()) {
293 // If socket is shared, AllocationSequence will receive the packet.
294 socket_->SignalReadPacket.connect(this, &TurnPort::OnReadPacket);
295 }
296
297 socket_->SignalReadyToSend.connect(this, &TurnPort::OnReadyToSend);
298
299 if (server_address_.proto == PROTO_TCP) {
300 socket_->SignalConnect.connect(this, &TurnPort::OnSocketConnect);
301 socket_->SignalClose.connect(this, &TurnPort::OnSocketClose);
302 }
303 return true;
304}
305
306void TurnPort::OnSocketConnect(rtc::AsyncPacketSocket* socket) {
307 ASSERT(server_address_.proto == PROTO_TCP);
308 // Do not use this port if the socket bound to a different address than
309 // the one we asked for. This is seen in Chrome, where TCP sockets cannot be
310 // given a binding address, and the platform is expected to pick the
311 // correct local address.
guoweis@webrtc.org4fba2932014-12-18 04:45:05 +0000312
313 // Further, to workaround issue 3927 in which a proxy is forcing TCP bound to
314 // localhost only, we're allowing Loopback IP even if it's not the same as the
315 // local Turn port.
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000316 if (socket->GetLocalAddress().ipaddr() != ip()) {
guoweis@webrtc.org4fba2932014-12-18 04:45:05 +0000317 if (socket->GetLocalAddress().IsLoopbackIP()) {
318 LOG(LS_WARNING) << "Socket is bound to a different address:"
319 << socket->GetLocalAddress().ipaddr().ToString()
320 << ", rather then the local port:" << ip().ToString()
321 << ". Still allowing it since it's localhost.";
322 } else {
323 LOG(LS_WARNING) << "Socket is bound to a different address:"
324 << socket->GetLocalAddress().ipaddr().ToString()
325 << ", rather then the local port:" << ip().ToString()
326 << ". Discarding TURN port.";
327 OnAllocateError();
328 return;
329 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000330 }
331
332 if (server_address_.address.IsUnresolved()) {
333 server_address_.address = socket_->GetRemoteAddress();
334 }
335
336 LOG(LS_INFO) << "TurnPort connected to " << socket->GetRemoteAddress()
337 << " using tcp.";
338 SendRequest(new TurnAllocateRequest(this), 0);
339}
340
341void TurnPort::OnSocketClose(rtc::AsyncPacketSocket* socket, int error) {
342 LOG_J(LS_WARNING, this) << "Connection with server failed, error=" << error;
343 if (!connected_) {
344 OnAllocateError();
345 }
346}
347
348void TurnPort::OnAllocateMismatch() {
349 if (allocate_mismatch_retries_ >= MAX_ALLOCATE_MISMATCH_RETRIES) {
350 LOG_J(LS_WARNING, this) << "Giving up on the port after "
351 << allocate_mismatch_retries_
352 << " retries for STUN_ERROR_ALLOCATION_MISMATCH";
353 OnAllocateError();
354 return;
355 }
356
357 LOG_J(LS_INFO, this) << "Allocating a new socket after "
358 << "STUN_ERROR_ALLOCATION_MISMATCH, retry = "
359 << allocate_mismatch_retries_ + 1;
360 if (SharedSocket()) {
361 ResetSharedSocket();
362 } else {
363 delete socket_;
364 }
365 socket_ = NULL;
366
367 PrepareAddress();
368 ++allocate_mismatch_retries_;
369}
370
371Connection* TurnPort::CreateConnection(const Candidate& address,
372 CandidateOrigin origin) {
373 // TURN-UDP can only connect to UDP candidates.
374 if (address.protocol() != UDP_PROTOCOL_NAME) {
375 return NULL;
376 }
377
378 if (!IsCompatibleAddress(address.address())) {
379 return NULL;
380 }
381
382 // Create an entry, if needed, so we can get our permissions set up correctly.
383 CreateEntry(address.address());
384
385 // A TURN port will have two candiates, STUN and TURN. STUN may not
386 // present in all cases. If present stun candidate will be added first
387 // and TURN candidate later.
388 for (size_t index = 0; index < Candidates().size(); ++index) {
389 if (Candidates()[index].type() == RELAY_PORT_TYPE) {
390 ProxyConnection* conn = new ProxyConnection(this, index, address);
391 conn->SignalDestroyed.connect(this, &TurnPort::OnConnectionDestroyed);
392 AddConnection(conn);
393 return conn;
394 }
395 }
396 return NULL;
397}
398
399int TurnPort::SetOption(rtc::Socket::Option opt, int value) {
400 if (!socket_) {
401 // If socket is not created yet, these options will be applied during socket
402 // creation.
403 socket_options_[opt] = value;
404 return 0;
405 }
406 return socket_->SetOption(opt, value);
407}
408
409int TurnPort::GetOption(rtc::Socket::Option opt, int* value) {
410 if (!socket_) {
411 SocketOptionsMap::const_iterator it = socket_options_.find(opt);
412 if (it == socket_options_.end()) {
413 return -1;
414 }
415 *value = it->second;
416 return 0;
417 }
418
419 return socket_->GetOption(opt, value);
420}
421
422int TurnPort::GetError() {
423 return error_;
424}
425
426int TurnPort::SendTo(const void* data, size_t size,
427 const rtc::SocketAddress& addr,
428 const rtc::PacketOptions& options,
429 bool payload) {
430 // Try to find an entry for this specific address; we should have one.
431 TurnEntry* entry = FindEntry(addr);
432 ASSERT(entry != NULL);
433 if (!entry) {
434 return 0;
435 }
436
437 if (!connected()) {
438 error_ = EWOULDBLOCK;
439 return SOCKET_ERROR;
440 }
441
442 // Send the actual contents to the server using the usual mechanism.
443 int sent = entry->Send(data, size, payload, options);
444 if (sent <= 0) {
445 return SOCKET_ERROR;
446 }
447
448 // The caller of the function is expecting the number of user data bytes,
449 // rather than the size of the packet.
450 return static_cast<int>(size);
451}
452
453void TurnPort::OnReadPacket(
454 rtc::AsyncPacketSocket* socket, const char* data, size_t size,
455 const rtc::SocketAddress& remote_addr,
456 const rtc::PacketTime& packet_time) {
457 ASSERT(socket == socket_);
guoweis@webrtc.orgc51fb932014-12-18 00:30:55 +0000458
459 // This is to guard against a STUN response from previous server after
460 // alternative server redirection. TODO(guoweis): add a unit test for this
461 // race condition.
462 if (remote_addr != server_address_.address) {
463 LOG_J(LS_WARNING, this) << "Discarding TURN message from unknown address:"
464 << remote_addr.ToString()
465 << ", server_address_:"
466 << server_address_.address.ToString();
467 return;
468 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000469
470 // The message must be at least the size of a channel header.
471 if (size < TURN_CHANNEL_HEADER_SIZE) {
472 LOG_J(LS_WARNING, this) << "Received TURN message that was too short";
473 return;
474 }
475
476 // Check the message type, to see if is a Channel Data message.
477 // The message will either be channel data, a TURN data indication, or
478 // a response to a previous request.
479 uint16 msg_type = rtc::GetBE16(data);
480 if (IsTurnChannelData(msg_type)) {
481 HandleChannelData(msg_type, data, size, packet_time);
482 } else if (msg_type == TURN_DATA_INDICATION) {
483 HandleDataIndication(data, size, packet_time);
484 } else {
jiayl@webrtc.org511f8a82014-12-03 02:17:07 +0000485 if (SharedSocket() &&
486 (msg_type == STUN_BINDING_RESPONSE ||
487 msg_type == STUN_BINDING_ERROR_RESPONSE)) {
488 LOG_J(LS_VERBOSE, this) <<
489 "Ignoring STUN binding response message on shared socket.";
490 return;
491 }
492
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000493 // This must be a response for one of our requests.
494 // Check success responses, but not errors, for MESSAGE-INTEGRITY.
495 if (IsStunSuccessResponseType(msg_type) &&
496 !StunMessage::ValidateMessageIntegrity(data, size, hash())) {
497 LOG_J(LS_WARNING, this) << "Received TURN message with invalid "
498 << "message integrity, msg_type=" << msg_type;
499 return;
500 }
501 request_manager_.CheckResponse(data, size);
502 }
503}
504
505void TurnPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
506 if (connected_) {
507 Port::OnReadyToSend();
508 }
509}
510
511
512// Update current server address port with the alternate server address port.
513bool TurnPort::SetAlternateServer(const rtc::SocketAddress& address) {
514 // Check if we have seen this address before and reject if we did.
515 AttemptedServerSet::iterator iter = attempted_server_addresses_.find(address);
516 if (iter != attempted_server_addresses_.end()) {
517 LOG_J(LS_WARNING, this) << "Redirection to ["
518 << address.ToSensitiveString()
519 << "] ignored, allocation failed.";
520 return false;
521 }
522
523 // If protocol family of server address doesn't match with local, return.
524 if (!IsCompatibleAddress(address)) {
525 LOG(LS_WARNING) << "Server IP address family does not match with "
526 << "local host address family type";
527 return false;
528 }
529
530 LOG_J(LS_INFO, this) << "Redirecting from TURN server ["
531 << server_address_.address.ToSensitiveString()
532 << "] to TURN server ["
533 << address.ToSensitiveString()
534 << "]";
535 server_address_ = ProtocolAddress(address, server_address_.proto,
536 server_address_.secure);
537
538 // Insert the current address to prevent redirection pingpong.
539 attempted_server_addresses_.insert(server_address_.address);
540 return true;
541}
542
543void TurnPort::ResolveTurnAddress(const rtc::SocketAddress& address) {
544 if (resolver_)
545 return;
546
547 resolver_ = socket_factory()->CreateAsyncResolver();
548 resolver_->SignalDone.connect(this, &TurnPort::OnResolveResult);
549 resolver_->Start(address);
550}
551
552void TurnPort::OnResolveResult(rtc::AsyncResolverInterface* resolver) {
553 ASSERT(resolver == resolver_);
554 // If DNS resolve is failed when trying to connect to the server using TCP,
555 // one of the reason could be due to DNS queries blocked by firewall.
556 // In such cases we will try to connect to the server with hostname, assuming
557 // socket layer will resolve the hostname through a HTTP proxy (if any).
558 if (resolver_->GetError() != 0 && server_address_.proto == PROTO_TCP) {
559 if (!CreateTurnClientSocket()) {
560 OnAllocateError();
561 }
562 return;
563 }
564
565 // Copy the original server address in |resolved_address|. For TLS based
566 // sockets we need hostname along with resolved address.
567 rtc::SocketAddress resolved_address = server_address_.address;
568 if (resolver_->GetError() != 0 ||
569 !resolver_->GetResolvedAddress(ip().family(), &resolved_address)) {
570 LOG_J(LS_WARNING, this) << "TURN host lookup received error "
571 << resolver_->GetError();
572 error_ = resolver_->GetError();
573 OnAllocateError();
574 return;
575 }
576 // Signal needs both resolved and unresolved address. After signal is sent
577 // we can copy resolved address back into |server_address_|.
578 SignalResolvedServerAddress(this, server_address_.address,
579 resolved_address);
580 server_address_.address = resolved_address;
581 PrepareAddress();
582}
583
584void TurnPort::OnSendStunPacket(const void* data, size_t size,
585 StunRequest* request) {
586 rtc::PacketOptions options(DefaultDscpValue());
587 if (Send(data, size, options) < 0) {
588 LOG_J(LS_ERROR, this) << "Failed to send TURN message, err="
589 << socket_->GetError();
590 }
591}
592
593void TurnPort::OnStunAddress(const rtc::SocketAddress& address) {
594 // STUN Port will discover STUN candidate, as it's supplied with first TURN
595 // server address.
596 // Why not using this address? - P2PTransportChannel will start creating
597 // connections after first candidate, which means it could start creating the
598 // connections before TURN candidate added. For that to handle, we need to
599 // supply STUN candidate from this port to UDPPort, and TurnPort should have
600 // handle to UDPPort to pass back the address.
601}
602
603void TurnPort::OnAllocateSuccess(const rtc::SocketAddress& address,
604 const rtc::SocketAddress& stun_address) {
605 connected_ = true;
606
607 rtc::SocketAddress related_address = stun_address;
608 if (!(candidate_filter() & CF_REFLEXIVE)) {
609 // If candidate filter only allows relay type of address, empty raddr to
610 // avoid local address leakage.
611 related_address = rtc::EmptySocketAddressWithFamily(stun_address.family());
612 }
613
614 // For relayed candidate, Base is the candidate itself.
615 AddAddress(address, // Candidate address.
616 address, // Base address.
617 related_address, // Related address.
618 UDP_PROTOCOL_NAME,
619 "", // TCP canddiate type, empty for turn candidates.
620 RELAY_PORT_TYPE,
621 GetRelayPreference(server_address_.proto, server_address_.secure),
622 server_priority_,
623 true);
624}
625
626void TurnPort::OnAllocateError() {
627 // We will send SignalPortError asynchronously as this can be sent during
628 // port initialization. This way it will not be blocking other port
629 // creation.
630 thread()->Post(this, MSG_ERROR);
631}
632
633void TurnPort::OnMessage(rtc::Message* message) {
634 if (message->message_id == MSG_ERROR) {
635 SignalPortError(this);
636 return;
637 } else if (message->message_id == MSG_ALLOCATE_MISMATCH) {
638 OnAllocateMismatch();
639 return;
640 }
641
642 Port::OnMessage(message);
643}
644
645void TurnPort::OnAllocateRequestTimeout() {
646 OnAllocateError();
647}
648
649void TurnPort::HandleDataIndication(const char* data, size_t size,
650 const rtc::PacketTime& packet_time) {
651 // Read in the message, and process according to RFC5766, Section 10.4.
652 rtc::ByteBuffer buf(data, size);
653 TurnMessage msg;
654 if (!msg.Read(&buf)) {
655 LOG_J(LS_WARNING, this) << "Received invalid TURN data indication";
656 return;
657 }
658
659 // Check mandatory attributes.
660 const StunAddressAttribute* addr_attr =
661 msg.GetAddress(STUN_ATTR_XOR_PEER_ADDRESS);
662 if (!addr_attr) {
663 LOG_J(LS_WARNING, this) << "Missing STUN_ATTR_XOR_PEER_ADDRESS attribute "
664 << "in data indication.";
665 return;
666 }
667
668 const StunByteStringAttribute* data_attr =
669 msg.GetByteString(STUN_ATTR_DATA);
670 if (!data_attr) {
671 LOG_J(LS_WARNING, this) << "Missing STUN_ATTR_DATA attribute in "
672 << "data indication.";
673 return;
674 }
675
676 // Verify that the data came from somewhere we think we have a permission for.
677 rtc::SocketAddress ext_addr(addr_attr->GetAddress());
678 if (!HasPermission(ext_addr.ipaddr())) {
679 LOG_J(LS_WARNING, this) << "Received TURN data indication with invalid "
680 << "peer address, addr="
681 << ext_addr.ToSensitiveString();
682 return;
683 }
684
685 DispatchPacket(data_attr->bytes(), data_attr->length(), ext_addr,
686 PROTO_UDP, packet_time);
687}
688
689void TurnPort::HandleChannelData(int channel_id, const char* data,
690 size_t size,
691 const rtc::PacketTime& packet_time) {
692 // Read the message, and process according to RFC5766, Section 11.6.
693 // 0 1 2 3
694 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
695 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
696 // | Channel Number | Length |
697 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
698 // | |
699 // / Application Data /
700 // / /
701 // | |
702 // | +-------------------------------+
703 // | |
704 // +-------------------------------+
705
706 // Extract header fields from the message.
707 uint16 len = rtc::GetBE16(data + 2);
708 if (len > size - TURN_CHANNEL_HEADER_SIZE) {
709 LOG_J(LS_WARNING, this) << "Received TURN channel data message with "
710 << "incorrect length, len=" << len;
711 return;
712 }
713 // Allowing messages larger than |len|, as ChannelData can be padded.
714
715 TurnEntry* entry = FindEntry(channel_id);
716 if (!entry) {
717 LOG_J(LS_WARNING, this) << "Received TURN channel data message for invalid "
718 << "channel, channel_id=" << channel_id;
719 return;
720 }
721
722 DispatchPacket(data + TURN_CHANNEL_HEADER_SIZE, len, entry->address(),
723 PROTO_UDP, packet_time);
724}
725
726void TurnPort::DispatchPacket(const char* data, size_t size,
727 const rtc::SocketAddress& remote_addr,
728 ProtocolType proto, const rtc::PacketTime& packet_time) {
729 if (Connection* conn = GetConnection(remote_addr)) {
730 conn->OnReadPacket(data, size, packet_time);
731 } else {
732 Port::OnReadPacket(data, size, remote_addr, proto);
733 }
734}
735
736bool TurnPort::ScheduleRefresh(int lifetime) {
737 // Lifetime is in seconds; we schedule a refresh for one minute less.
738 if (lifetime < 2 * 60) {
739 LOG_J(LS_WARNING, this) << "Received response with lifetime that was "
740 << "too short, lifetime=" << lifetime;
741 return false;
742 }
743
744 SendRequest(new TurnRefreshRequest(this), (lifetime - 60) * 1000);
745 return true;
746}
747
748void TurnPort::SendRequest(StunRequest* req, int delay) {
749 request_manager_.SendDelayed(req, delay);
750}
751
752void TurnPort::AddRequestAuthInfo(StunMessage* msg) {
753 // If we've gotten the necessary data from the server, add it to our request.
754 VERIFY(!hash_.empty());
755 VERIFY(msg->AddAttribute(new StunByteStringAttribute(
756 STUN_ATTR_USERNAME, credentials_.username)));
757 VERIFY(msg->AddAttribute(new StunByteStringAttribute(
758 STUN_ATTR_REALM, realm_)));
759 VERIFY(msg->AddAttribute(new StunByteStringAttribute(
760 STUN_ATTR_NONCE, nonce_)));
761 VERIFY(msg->AddMessageIntegrity(hash()));
762}
763
764int TurnPort::Send(const void* data, size_t len,
765 const rtc::PacketOptions& options) {
766 return socket_->SendTo(data, len, server_address_.address, options);
767}
768
769void TurnPort::UpdateHash() {
770 VERIFY(ComputeStunCredentialHash(credentials_.username, realm_,
771 credentials_.password, &hash_));
772}
773
774bool TurnPort::UpdateNonce(StunMessage* response) {
775 // When stale nonce error received, we should update
776 // hash and store realm and nonce.
777 // Check the mandatory attributes.
778 const StunByteStringAttribute* realm_attr =
779 response->GetByteString(STUN_ATTR_REALM);
780 if (!realm_attr) {
781 LOG(LS_ERROR) << "Missing STUN_ATTR_REALM attribute in "
782 << "stale nonce error response.";
783 return false;
784 }
785 set_realm(realm_attr->GetString());
786
787 const StunByteStringAttribute* nonce_attr =
788 response->GetByteString(STUN_ATTR_NONCE);
789 if (!nonce_attr) {
790 LOG(LS_ERROR) << "Missing STUN_ATTR_NONCE attribute in "
791 << "stale nonce error response.";
792 return false;
793 }
794 set_nonce(nonce_attr->GetString());
795 return true;
796}
797
798static bool MatchesIP(TurnEntry* e, rtc::IPAddress ipaddr) {
799 return e->address().ipaddr() == ipaddr;
800}
801bool TurnPort::HasPermission(const rtc::IPAddress& ipaddr) const {
802 return (std::find_if(entries_.begin(), entries_.end(),
803 std::bind2nd(std::ptr_fun(MatchesIP), ipaddr)) != entries_.end());
804}
805
806static bool MatchesAddress(TurnEntry* e, rtc::SocketAddress addr) {
807 return e->address() == addr;
808}
809TurnEntry* TurnPort::FindEntry(const rtc::SocketAddress& addr) const {
810 EntryList::const_iterator it = std::find_if(entries_.begin(), entries_.end(),
811 std::bind2nd(std::ptr_fun(MatchesAddress), addr));
812 return (it != entries_.end()) ? *it : NULL;
813}
814
815static bool MatchesChannelId(TurnEntry* e, int id) {
816 return e->channel_id() == id;
817}
818TurnEntry* TurnPort::FindEntry(int channel_id) const {
819 EntryList::const_iterator it = std::find_if(entries_.begin(), entries_.end(),
820 std::bind2nd(std::ptr_fun(MatchesChannelId), channel_id));
821 return (it != entries_.end()) ? *it : NULL;
822}
823
824TurnEntry* TurnPort::CreateEntry(const rtc::SocketAddress& addr) {
825 ASSERT(FindEntry(addr) == NULL);
826 TurnEntry* entry = new TurnEntry(this, next_channel_number_++, addr);
827 entries_.push_back(entry);
828 return entry;
829}
830
831void TurnPort::DestroyEntry(const rtc::SocketAddress& addr) {
832 TurnEntry* entry = FindEntry(addr);
833 ASSERT(entry != NULL);
834 entry->SignalDestroyed(entry);
835 entries_.remove(entry);
836 delete entry;
837}
838
839void TurnPort::OnConnectionDestroyed(Connection* conn) {
840 // Destroying TurnEntry for the connection, which is already destroyed.
841 DestroyEntry(conn->remote_candidate().address());
842}
843
844TurnAllocateRequest::TurnAllocateRequest(TurnPort* port)
845 : StunRequest(new TurnMessage()),
846 port_(port) {
847}
848
849void TurnAllocateRequest::Prepare(StunMessage* request) {
850 // Create the request as indicated in RFC 5766, Section 6.1.
851 request->SetType(TURN_ALLOCATE_REQUEST);
852 StunUInt32Attribute* transport_attr = StunAttribute::CreateUInt32(
853 STUN_ATTR_REQUESTED_TRANSPORT);
854 transport_attr->SetValue(IPPROTO_UDP << 24);
855 VERIFY(request->AddAttribute(transport_attr));
856 if (!port_->hash().empty()) {
857 port_->AddRequestAuthInfo(request);
858 }
859}
860
861void TurnAllocateRequest::OnResponse(StunMessage* response) {
862 // Check mandatory attributes as indicated in RFC5766, Section 6.3.
863 const StunAddressAttribute* mapped_attr =
864 response->GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
865 if (!mapped_attr) {
866 LOG_J(LS_WARNING, port_) << "Missing STUN_ATTR_XOR_MAPPED_ADDRESS "
867 << "attribute in allocate success response";
868 return;
869 }
870 // Using XOR-Mapped-Address for stun.
871 port_->OnStunAddress(mapped_attr->GetAddress());
872
873 const StunAddressAttribute* relayed_attr =
874 response->GetAddress(STUN_ATTR_XOR_RELAYED_ADDRESS);
875 if (!relayed_attr) {
876 LOG_J(LS_WARNING, port_) << "Missing STUN_ATTR_XOR_RELAYED_ADDRESS "
877 << "attribute in allocate success response";
878 return;
879 }
880
881 const StunUInt32Attribute* lifetime_attr =
882 response->GetUInt32(STUN_ATTR_TURN_LIFETIME);
883 if (!lifetime_attr) {
884 LOG_J(LS_WARNING, port_) << "Missing STUN_ATTR_TURN_LIFETIME attribute in "
885 << "allocate success response";
886 return;
887 }
888 // Notify the port the allocate succeeded, and schedule a refresh request.
889 port_->OnAllocateSuccess(relayed_attr->GetAddress(),
890 mapped_attr->GetAddress());
891 port_->ScheduleRefresh(lifetime_attr->value());
892}
893
894void TurnAllocateRequest::OnErrorResponse(StunMessage* response) {
895 // Process error response according to RFC5766, Section 6.4.
896 const StunErrorCodeAttribute* error_code = response->GetErrorCode();
897 switch (error_code->code()) {
898 case STUN_ERROR_UNAUTHORIZED: // Unauthrorized.
899 OnAuthChallenge(response, error_code->code());
900 break;
901 case STUN_ERROR_TRY_ALTERNATE:
902 OnTryAlternate(response, error_code->code());
903 break;
904 case STUN_ERROR_ALLOCATION_MISMATCH:
905 // We must handle this error async because trying to delete the socket in
906 // OnErrorResponse will cause a deadlock on the socket.
907 port_->thread()->Post(port_, TurnPort::MSG_ALLOCATE_MISMATCH);
908 break;
909 default:
910 LOG_J(LS_WARNING, port_) << "Allocate response error, code="
911 << error_code->code();
912 port_->OnAllocateError();
913 }
914}
915
916void TurnAllocateRequest::OnTimeout() {
917 LOG_J(LS_WARNING, port_) << "Allocate request timeout";
918 port_->OnAllocateRequestTimeout();
919}
920
921void TurnAllocateRequest::OnAuthChallenge(StunMessage* response, int code) {
922 // If we failed to authenticate even after we sent our credentials, fail hard.
923 if (code == STUN_ERROR_UNAUTHORIZED && !port_->hash().empty()) {
924 LOG_J(LS_WARNING, port_) << "Failed to authenticate with the server "
925 << "after challenge.";
926 port_->OnAllocateError();
927 return;
928 }
929
930 // Check the mandatory attributes.
931 const StunByteStringAttribute* realm_attr =
932 response->GetByteString(STUN_ATTR_REALM);
933 if (!realm_attr) {
934 LOG_J(LS_WARNING, port_) << "Missing STUN_ATTR_REALM attribute in "
935 << "allocate unauthorized response.";
936 return;
937 }
938 port_->set_realm(realm_attr->GetString());
939
940 const StunByteStringAttribute* nonce_attr =
941 response->GetByteString(STUN_ATTR_NONCE);
942 if (!nonce_attr) {
943 LOG_J(LS_WARNING, port_) << "Missing STUN_ATTR_NONCE attribute in "
944 << "allocate unauthorized response.";
945 return;
946 }
947 port_->set_nonce(nonce_attr->GetString());
948
949 // Send another allocate request, with the received realm and nonce values.
950 port_->SendRequest(new TurnAllocateRequest(port_), 0);
951}
952
953void TurnAllocateRequest::OnTryAlternate(StunMessage* response, int code) {
954 // TODO(guoweis): Currently, we only support UDP redirect
955 if (port_->server_address().proto != PROTO_UDP) {
956 LOG_J(LS_WARNING, port_) << "Receiving 300 Alternate Server on non-UDP "
957 << "allocating request from ["
958 << port_->server_address().address.ToSensitiveString()
959 << "], failed as currently not supported";
960 port_->OnAllocateError();
961 return;
962 }
963
964 // According to RFC 5389 section 11, there are use cases where
965 // authentication of response is not possible, we're not validating
966 // message integrity.
967
968 // Get the alternate server address attribute value.
969 const StunAddressAttribute* alternate_server_attr =
970 response->GetAddress(STUN_ATTR_ALTERNATE_SERVER);
971 if (!alternate_server_attr) {
972 LOG_J(LS_WARNING, port_) << "Missing STUN_ATTR_ALTERNATE_SERVER "
973 << "attribute in try alternate error response";
974 port_->OnAllocateError();
975 return;
976 }
977 if (!port_->SetAlternateServer(alternate_server_attr->GetAddress())) {
978 port_->OnAllocateError();
979 return;
980 }
981
982 // Check the attributes.
983 const StunByteStringAttribute* realm_attr =
984 response->GetByteString(STUN_ATTR_REALM);
985 if (realm_attr) {
986 LOG_J(LS_INFO, port_) << "Applying STUN_ATTR_REALM attribute in "
987 << "try alternate error response.";
988 port_->set_realm(realm_attr->GetString());
989 }
990
991 const StunByteStringAttribute* nonce_attr =
992 response->GetByteString(STUN_ATTR_NONCE);
993 if (nonce_attr) {
994 LOG_J(LS_INFO, port_) << "Applying STUN_ATTR_NONCE attribute in "
995 << "try alternate error response.";
996 port_->set_nonce(nonce_attr->GetString());
997 }
998
999 // Send another allocate request to alternate server,
1000 // with the received realm and nonce values.
1001 port_->SendRequest(new TurnAllocateRequest(port_), 0);
1002}
1003
1004TurnRefreshRequest::TurnRefreshRequest(TurnPort* port)
1005 : StunRequest(new TurnMessage()),
1006 port_(port) {
1007}
1008
1009void TurnRefreshRequest::Prepare(StunMessage* request) {
1010 // Create the request as indicated in RFC 5766, Section 7.1.
1011 // No attributes need to be included.
1012 request->SetType(TURN_REFRESH_REQUEST);
1013 port_->AddRequestAuthInfo(request);
1014}
1015
1016void TurnRefreshRequest::OnResponse(StunMessage* response) {
1017 // Check mandatory attributes as indicated in RFC5766, Section 7.3.
1018 const StunUInt32Attribute* lifetime_attr =
1019 response->GetUInt32(STUN_ATTR_TURN_LIFETIME);
1020 if (!lifetime_attr) {
1021 LOG_J(LS_WARNING, port_) << "Missing STUN_ATTR_TURN_LIFETIME attribute in "
1022 << "refresh success response.";
1023 return;
1024 }
1025
1026 // Schedule a refresh based on the returned lifetime value.
1027 port_->ScheduleRefresh(lifetime_attr->value());
1028}
1029
1030void TurnRefreshRequest::OnErrorResponse(StunMessage* response) {
1031 const StunErrorCodeAttribute* error_code = response->GetErrorCode();
1032 LOG_J(LS_WARNING, port_) << "Refresh response error, code="
1033 << error_code->code();
1034
1035 if (error_code->code() == STUN_ERROR_STALE_NONCE) {
1036 if (port_->UpdateNonce(response)) {
1037 // Send RefreshRequest immediately.
1038 port_->SendRequest(new TurnRefreshRequest(port_), 0);
1039 }
1040 }
1041}
1042
1043void TurnRefreshRequest::OnTimeout() {
1044}
1045
1046TurnCreatePermissionRequest::TurnCreatePermissionRequest(
1047 TurnPort* port, TurnEntry* entry,
1048 const rtc::SocketAddress& ext_addr)
1049 : StunRequest(new TurnMessage()),
1050 port_(port),
1051 entry_(entry),
1052 ext_addr_(ext_addr) {
1053 entry_->SignalDestroyed.connect(
1054 this, &TurnCreatePermissionRequest::OnEntryDestroyed);
1055}
1056
1057void TurnCreatePermissionRequest::Prepare(StunMessage* request) {
1058 // Create the request as indicated in RFC5766, Section 9.1.
1059 request->SetType(TURN_CREATE_PERMISSION_REQUEST);
1060 VERIFY(request->AddAttribute(new StunXorAddressAttribute(
1061 STUN_ATTR_XOR_PEER_ADDRESS, ext_addr_)));
1062 port_->AddRequestAuthInfo(request);
1063}
1064
1065void TurnCreatePermissionRequest::OnResponse(StunMessage* response) {
1066 if (entry_) {
1067 entry_->OnCreatePermissionSuccess();
1068 }
1069}
1070
1071void TurnCreatePermissionRequest::OnErrorResponse(StunMessage* response) {
1072 if (entry_) {
1073 const StunErrorCodeAttribute* error_code = response->GetErrorCode();
1074 entry_->OnCreatePermissionError(response, error_code->code());
1075 }
1076}
1077
1078void TurnCreatePermissionRequest::OnTimeout() {
1079 LOG_J(LS_WARNING, port_) << "Create permission timeout";
1080}
1081
1082void TurnCreatePermissionRequest::OnEntryDestroyed(TurnEntry* entry) {
1083 ASSERT(entry_ == entry);
1084 entry_ = NULL;
1085}
1086
1087TurnChannelBindRequest::TurnChannelBindRequest(
1088 TurnPort* port, TurnEntry* entry,
1089 int channel_id, const rtc::SocketAddress& ext_addr)
1090 : StunRequest(new TurnMessage()),
1091 port_(port),
1092 entry_(entry),
1093 channel_id_(channel_id),
1094 ext_addr_(ext_addr) {
1095 entry_->SignalDestroyed.connect(
1096 this, &TurnChannelBindRequest::OnEntryDestroyed);
1097}
1098
1099void TurnChannelBindRequest::Prepare(StunMessage* request) {
1100 // Create the request as indicated in RFC5766, Section 11.1.
1101 request->SetType(TURN_CHANNEL_BIND_REQUEST);
1102 VERIFY(request->AddAttribute(new StunUInt32Attribute(
1103 STUN_ATTR_CHANNEL_NUMBER, channel_id_ << 16)));
1104 VERIFY(request->AddAttribute(new StunXorAddressAttribute(
1105 STUN_ATTR_XOR_PEER_ADDRESS, ext_addr_)));
1106 port_->AddRequestAuthInfo(request);
1107}
1108
1109void TurnChannelBindRequest::OnResponse(StunMessage* response) {
1110 if (entry_) {
1111 entry_->OnChannelBindSuccess();
1112 // Refresh the channel binding just under the permission timeout
1113 // threshold. The channel binding has a longer lifetime, but
1114 // this is the easiest way to keep both the channel and the
1115 // permission from expiring.
1116 entry_->SendChannelBindRequest(TURN_PERMISSION_TIMEOUT - 60 * 1000);
1117 }
1118}
1119
1120void TurnChannelBindRequest::OnErrorResponse(StunMessage* response) {
1121 if (entry_) {
1122 const StunErrorCodeAttribute* error_code = response->GetErrorCode();
1123 entry_->OnChannelBindError(response, error_code->code());
1124 }
1125}
1126
1127void TurnChannelBindRequest::OnTimeout() {
1128 LOG_J(LS_WARNING, port_) << "Channel bind timeout";
1129}
1130
1131void TurnChannelBindRequest::OnEntryDestroyed(TurnEntry* entry) {
1132 ASSERT(entry_ == entry);
1133 entry_ = NULL;
1134}
1135
1136TurnEntry::TurnEntry(TurnPort* port, int channel_id,
1137 const rtc::SocketAddress& ext_addr)
1138 : port_(port),
1139 channel_id_(channel_id),
1140 ext_addr_(ext_addr),
1141 state_(STATE_UNBOUND) {
1142 // Creating permission for |ext_addr_|.
1143 SendCreatePermissionRequest();
1144}
1145
1146void TurnEntry::SendCreatePermissionRequest() {
1147 port_->SendRequest(new TurnCreatePermissionRequest(
1148 port_, this, ext_addr_), 0);
1149}
1150
1151void TurnEntry::SendChannelBindRequest(int delay) {
1152 port_->SendRequest(new TurnChannelBindRequest(
1153 port_, this, channel_id_, ext_addr_), delay);
1154}
1155
1156int TurnEntry::Send(const void* data, size_t size, bool payload,
1157 const rtc::PacketOptions& options) {
1158 rtc::ByteBuffer buf;
1159 if (state_ != STATE_BOUND) {
1160 // If we haven't bound the channel yet, we have to use a Send Indication.
1161 TurnMessage msg;
1162 msg.SetType(TURN_SEND_INDICATION);
1163 msg.SetTransactionID(
1164 rtc::CreateRandomString(kStunTransactionIdLength));
1165 VERIFY(msg.AddAttribute(new StunXorAddressAttribute(
1166 STUN_ATTR_XOR_PEER_ADDRESS, ext_addr_)));
1167 VERIFY(msg.AddAttribute(new StunByteStringAttribute(
1168 STUN_ATTR_DATA, data, size)));
1169 VERIFY(msg.Write(&buf));
1170
1171 // If we're sending real data, request a channel bind that we can use later.
1172 if (state_ == STATE_UNBOUND && payload) {
1173 SendChannelBindRequest(0);
1174 state_ = STATE_BINDING;
1175 }
1176 } else {
1177 // If the channel is bound, we can send the data as a Channel Message.
1178 buf.WriteUInt16(channel_id_);
1179 buf.WriteUInt16(static_cast<uint16>(size));
1180 buf.WriteBytes(reinterpret_cast<const char*>(data), size);
1181 }
1182 return port_->Send(buf.Data(), buf.Length(), options);
1183}
1184
1185void TurnEntry::OnCreatePermissionSuccess() {
1186 LOG_J(LS_INFO, port_) << "Create permission for "
1187 << ext_addr_.ToSensitiveString()
1188 << " succeeded";
1189 // For success result code will be 0.
1190 port_->SignalCreatePermissionResult(port_, ext_addr_, 0);
1191}
1192
1193void TurnEntry::OnCreatePermissionError(StunMessage* response, int code) {
1194 LOG_J(LS_WARNING, port_) << "Create permission for "
1195 << ext_addr_.ToSensitiveString()
1196 << " failed, code=" << code;
1197 if (code == STUN_ERROR_STALE_NONCE) {
1198 if (port_->UpdateNonce(response)) {
1199 SendCreatePermissionRequest();
1200 }
1201 } else {
1202 // Send signal with error code.
1203 port_->SignalCreatePermissionResult(port_, ext_addr_, code);
1204 }
1205}
1206
1207void TurnEntry::OnChannelBindSuccess() {
1208 LOG_J(LS_INFO, port_) << "Channel bind for " << ext_addr_.ToSensitiveString()
1209 << " succeeded";
1210 ASSERT(state_ == STATE_BINDING || state_ == STATE_BOUND);
1211 state_ = STATE_BOUND;
1212}
1213
1214void TurnEntry::OnChannelBindError(StunMessage* response, int code) {
1215 // TODO(mallinath) - Implement handling of error response for channel
1216 // bind request as per http://tools.ietf.org/html/rfc5766#section-11.3
1217 LOG_J(LS_WARNING, port_) << "Channel bind for "
1218 << ext_addr_.ToSensitiveString()
1219 << " failed, code=" << code;
1220 if (code == STUN_ERROR_STALE_NONCE) {
1221 if (port_->UpdateNonce(response)) {
1222 // Send channel bind request with fresh nonce.
1223 SendChannelBindRequest(0);
1224 }
1225 }
1226}
1227
1228} // namespace cricket