Note: We no longer publish the latest version of our code here. We primarily use a kumc-bmi github organization. The heron ETL repository, in particular, is not public. Peers in the informatics community should see MultiSiteDev for details on requesting access.

source: webrtc/talk/p2p/base/turnport.cc @ 0:4bda6873e34c

pub_scrub_3792 tip
Last change on this file since 0:4bda6873e34c was 0:4bda6873e34c, checked in by Michael Prittie <mprittie@…>, 6 years ago

Scrubbed password for publication.

File size: 33.5 KB
Line 
1/*
2 * libjingle
3 * Copyright 2012, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/p2p/base/turnport.h"
29
30#include <functional>
31
32#include "talk/base/asyncpacketsocket.h"
33#include "talk/base/byteorder.h"
34#include "talk/base/common.h"
35#include "talk/base/logging.h"
36#include "talk/base/nethelpers.h"
37#include "talk/base/socketaddress.h"
38#include "talk/base/stringencode.h"
39#include "talk/p2p/base/common.h"
40#include "talk/p2p/base/stun.h"
41
42namespace cricket {
43
44// TODO(juberti): Move to stun.h when relay messages have been renamed.
45static const int TURN_ALLOCATE_REQUEST = STUN_ALLOCATE_REQUEST;
46static const int TURN_ALLOCATE_ERROR_RESPONSE = STUN_ALLOCATE_ERROR_RESPONSE;
47
48// TODO(juberti): Extract to turnmessage.h
49static const int TURN_DEFAULT_PORT = 3478;
50static const int TURN_CHANNEL_NUMBER_START = 0x4000;
51static const int TURN_PERMISSION_TIMEOUT = 5 * 60 * 1000;  // 5 minutes
52
53static const size_t TURN_CHANNEL_HEADER_SIZE = 4U;
54
55inline bool IsTurnChannelData(uint16 msg_type) {
56  return ((msg_type & 0xC000) == 0x4000);  // MSB are 0b01
57}
58
59static int GetRelayPreference(cricket::ProtocolType proto, bool secure) {
60  int relay_preference = ICE_TYPE_PREFERENCE_RELAY;
61  if (proto == cricket::PROTO_TCP) {
62    relay_preference -= 1;
63    if (secure)
64      relay_preference -= 1;
65  }
66
67  ASSERT(relay_preference >= 0);
68  return relay_preference;
69}
70
71class TurnAllocateRequest : public StunRequest {
72 public:
73  explicit TurnAllocateRequest(TurnPort* port);
74  virtual void Prepare(StunMessage* request);
75  virtual void OnResponse(StunMessage* response);
76  virtual void OnErrorResponse(StunMessage* response);
77  virtual void OnTimeout();
78
79 private:
80  // Handles authentication challenge from the server.
81  void OnAuthChallenge(StunMessage* response, int code);
82  void OnUnknownAttribute(StunMessage* response);
83
84  TurnPort* port_;
85};
86
87class TurnRefreshRequest : public StunRequest {
88 public:
89  explicit TurnRefreshRequest(TurnPort* port);
90  virtual void Prepare(StunMessage* request);
91  virtual void OnResponse(StunMessage* response);
92  virtual void OnErrorResponse(StunMessage* response);
93  virtual void OnTimeout();
94
95 private:
96  TurnPort* port_;
97};
98
99class TurnCreatePermissionRequest : public StunRequest,
100                                    public sigslot::has_slots<> {
101 public:
102  TurnCreatePermissionRequest(TurnPort* port, TurnEntry* entry,
103                              const talk_base::SocketAddress& ext_addr);
104  virtual void Prepare(StunMessage* request);
105  virtual void OnResponse(StunMessage* response);
106  virtual void OnErrorResponse(StunMessage* response);
107  virtual void OnTimeout();
108
109 private:
110  void OnEntryDestroyed(TurnEntry* entry);
111
112  TurnPort* port_;
113  TurnEntry* entry_;
114  talk_base::SocketAddress ext_addr_;
115};
116
117class TurnChannelBindRequest : public StunRequest,
118                               public sigslot::has_slots<> {
119 public:
120  TurnChannelBindRequest(TurnPort* port, TurnEntry* entry, int channel_id,
121                         const talk_base::SocketAddress& ext_addr);
122  virtual void Prepare(StunMessage* request);
123  virtual void OnResponse(StunMessage* response);
124  virtual void OnErrorResponse(StunMessage* response);
125  virtual void OnTimeout();
126
127 private:
128  void OnEntryDestroyed(TurnEntry* entry);
129
130  TurnPort* port_;
131  TurnEntry* entry_;
132  int channel_id_;
133  talk_base::SocketAddress ext_addr_;
134};
135
136// Manages a "connection" to a remote destination. We will attempt to bring up
137// a channel for this remote destination to reduce the overhead of sending data.
138class TurnEntry : public sigslot::has_slots<> {
139 public:
140  enum BindState { STATE_UNBOUND, STATE_BINDING, STATE_BOUND };
141  TurnEntry(TurnPort* port, int channel_id,
142            const talk_base::SocketAddress& ext_addr);
143
144  TurnPort* port() { return port_; }
145
146  int channel_id() const { return channel_id_; }
147  const talk_base::SocketAddress& address() const { return ext_addr_; }
148  BindState state() const { return state_; }
149
150  // Helper methods to send permission and channel bind requests.
151  void SendCreatePermissionRequest();
152  void SendChannelBindRequest(int delay);
153  // Sends a packet to the given destination address.
154  // This will wrap the packet in STUN if necessary.
155  int Send(const void* data, size_t size, bool payload,
156           talk_base::DiffServCodePoint dscp);
157
158  void OnCreatePermissionSuccess();
159  void OnCreatePermissionError(StunMessage* response, int code);
160  void OnChannelBindSuccess();
161  void OnChannelBindError(StunMessage* response, int code);
162  // Signal sent when TurnEntry is destroyed.
163  sigslot::signal1<TurnEntry*> SignalDestroyed;
164
165 private:
166  TurnPort* port_;
167  int channel_id_;
168  talk_base::SocketAddress ext_addr_;
169  BindState state_;
170};
171
172TurnPort::TurnPort(talk_base::Thread* thread,
173                   talk_base::PacketSocketFactory* factory,
174                   talk_base::Network* network,
175                   const talk_base::IPAddress& ip,
176                   int min_port, int max_port,
177                   const std::string& username,
178                   const std::string& password,
179                   const ProtocolAddress& server_address,
180                   const RelayCredentials& credentials)
181    : Port(thread, RELAY_PORT_TYPE, factory, network, ip, min_port, max_port,
182           username, password),
183      server_address_(server_address),
184      credentials_(credentials),
185      resolver_(NULL),
186      error_(0),
187      request_manager_(thread),
188      next_channel_number_(TURN_CHANNEL_NUMBER_START),
189      connected_(false) {
190  request_manager_.SignalSendPacket.connect(this, &TurnPort::OnSendStunPacket);
191}
192
193TurnPort::~TurnPort() {
194  // TODO(juberti): Should this even be necessary?
195  while (!entries_.empty()) {
196    DestroyEntry(entries_.front()->address());
197  }
198}
199
200void TurnPort::PrepareAddress() {
201  if (credentials_.username.empty() ||
202      credentials_.password.empty()) {
203    LOG(LS_ERROR) << "Allocation can't be started without setting the"
204                  << " TURN server credentials for the user.";
205    OnAllocateError();
206    return;
207  }
208
209  if (!server_address_.address.port()) {
210    // We will set default TURN port, if no port is set in the address.
211    server_address_.address.SetPort(TURN_DEFAULT_PORT);
212  }
213
214  if (server_address_.address.IsUnresolved()) {
215    ResolveTurnAddress(server_address_.address);
216  } else {
217    // If protocol family of server address doesn't match with local, return.
218    if (!IsCompatibleAddress(server_address_.address)) {
219      LOG(LS_ERROR) << "Server IP address family does not match with "
220                    << "local host address family type";
221      OnAllocateError();
222      return;
223    }
224
225    LOG_J(LS_INFO, this) << "Trying to connect to TURN server via "
226                         << ProtoToString(server_address_.proto) << " @ "
227                         << server_address_.address.ToSensitiveString();
228    if (server_address_.proto == PROTO_UDP) {
229      socket_.reset(socket_factory()->CreateUdpSocket(
230          talk_base::SocketAddress(ip(), 0), min_port(), max_port()));
231    } else if (server_address_.proto == PROTO_TCP) {
232      int opts = talk_base::PacketSocketFactory::OPT_STUN;
233      // If secure bit is enabled in server address, use TLS over TCP.
234      if (server_address_.secure) {
235        opts |= talk_base::PacketSocketFactory::OPT_TLS;
236      }
237
238      socket_.reset(socket_factory()->CreateClientTcpSocket(
239          talk_base::SocketAddress(ip(), 0), server_address_.address,
240          proxy(), user_agent(), opts));
241    }
242
243    if (!socket_) {
244      OnAllocateError();
245      return;
246    }
247
248    // Apply options if any.
249    for (SocketOptionsMap::iterator iter = socket_options_.begin();
250         iter != socket_options_.end(); ++iter) {
251      socket_->SetOption(iter->first, iter->second);
252    }
253
254    socket_->SignalReadPacket.connect(this, &TurnPort::OnReadPacket);
255    socket_->SignalReadyToSend.connect(this, &TurnPort::OnReadyToSend);
256
257    if (server_address_.proto == PROTO_TCP) {
258      socket_->SignalConnect.connect(this, &TurnPort::OnSocketConnect);
259      socket_->SignalClose.connect(this, &TurnPort::OnSocketClose);
260    } else {
261      // If its UDP, send AllocateRequest now.
262      // For TCP and TLS AllcateRequest will be sent by OnSocketConnect.
263      SendRequest(new TurnAllocateRequest(this), 0);
264    }
265  }
266}
267
268void TurnPort::OnSocketConnect(talk_base::AsyncPacketSocket* socket) {
269  LOG(LS_INFO) << "TurnPort connected to " << socket->GetRemoteAddress()
270               << " using tcp.";
271  SendRequest(new TurnAllocateRequest(this), 0);
272}
273
274void TurnPort::OnSocketClose(talk_base::AsyncPacketSocket* socket, int error) {
275  LOG_J(LS_WARNING, this) << "Connection with server failed, error=" << error;
276  if (!connected_) {
277    OnAllocateError();
278  }
279}
280
281Connection* TurnPort::CreateConnection(const Candidate& address,
282                                       CandidateOrigin origin) {
283  // TURN-UDP can only connect to UDP candidates.
284  if (address.protocol() != UDP_PROTOCOL_NAME) {
285    return NULL;
286  }
287
288  if (!IsCompatibleAddress(address.address())) {
289    return NULL;
290  }
291
292  // Create an entry, if needed, so we can get our permissions set up correctly.
293  CreateEntry(address.address());
294
295  // TODO(juberti): The '0' index will need to change if we start gathering STUN
296  // candidates on this port.
297  ProxyConnection* conn = new ProxyConnection(this, 0, address);
298  conn->SignalDestroyed.connect(this, &TurnPort::OnConnectionDestroyed);
299  AddConnection(conn);
300  return conn;
301}
302
303int TurnPort::SetOption(talk_base::Socket::Option opt, int value) {
304  // DSCP option is not passed to the socket.
305  // TODO(mallinath) - After we have the support on socket,
306  // remove this specialization.
307  if (opt == talk_base::Socket::OPT_DSCP) {
308    SetDefaultDscpValue(static_cast<talk_base::DiffServCodePoint>(value));
309    return 0;
310  }
311
312  if (!socket_) {
313    // If socket is not created yet, these options will be applied during socket
314    // creation.
315    socket_options_[opt] = value;
316    return 0;
317  }
318  return socket_->SetOption(opt, value);
319}
320
321int TurnPort::GetOption(talk_base::Socket::Option opt, int* value) {
322  if (!socket_)
323    return -1;
324
325  return socket_->GetOption(opt, value);
326}
327
328int TurnPort::GetError() {
329  return error_;
330}
331
332int TurnPort::SendTo(const void* data, size_t size,
333                     const talk_base::SocketAddress& addr,
334                     talk_base::DiffServCodePoint dscp,
335                     bool payload) {
336  // Try to find an entry for this specific address; we should have one.
337  TurnEntry* entry = FindEntry(addr);
338  ASSERT(entry != NULL);
339  if (!entry) {
340    return 0;
341  }
342
343  if (!connected()) {
344    error_ = EWOULDBLOCK;
345    return SOCKET_ERROR;
346  }
347
348  // Send the actual contents to the server using the usual mechanism.
349  int sent = entry->Send(data, size, payload, dscp);
350  if (sent <= 0) {
351    return SOCKET_ERROR;
352  }
353
354  // The caller of the function is expecting the number of user data bytes,
355  // rather than the size of the packet.
356  return static_cast<int>(size);
357}
358
359void TurnPort::OnReadPacket(talk_base::AsyncPacketSocket* socket,
360                           const char* data, size_t size,
361                           const talk_base::SocketAddress& remote_addr) {
362  ASSERT(socket == socket_.get());
363  ASSERT(remote_addr == server_address_.address);
364
365  // The message must be at least the size of a channel header.
366  if (size < TURN_CHANNEL_HEADER_SIZE) {
367    LOG_J(LS_WARNING, this) << "Received TURN message that was too short";
368    return;
369  }
370
371  // Check the message type, to see if is a Channel Data message.
372  // The message will either be channel data, a TURN data indication, or
373  // a response to a previous request.
374  uint16 msg_type = talk_base::GetBE16(data);
375  if (IsTurnChannelData(msg_type)) {
376    HandleChannelData(msg_type, data, size);
377  } else if (msg_type == TURN_DATA_INDICATION) {
378    HandleDataIndication(data, size);
379  } else {
380    // This must be a response for one of our requests.
381    // Check success responses, but not errors, for MESSAGE-INTEGRITY.
382    if (IsStunSuccessResponseType(msg_type) &&
383        !StunMessage::ValidateMessageIntegrity(data, size, hash())) {
384      LOG_J(LS_WARNING, this) << "Received TURN message with invalid "
385                              << "message integrity, msg_type=" << msg_type;
386      return;
387    }
388    request_manager_.CheckResponse(data, size);
389  }
390}
391
392void TurnPort::OnReadyToSend(talk_base::AsyncPacketSocket* socket) {
393  if (connected_) {
394    Port::OnReadyToSend();
395  }
396}
397
398void TurnPort::ResolveTurnAddress(const talk_base::SocketAddress& address) {
399  if (resolver_)
400    return;
401
402  resolver_ = socket_factory()->CreateAsyncResolver();
403  resolver_->SignalDone.connect(this, &TurnPort::OnResolveResult);
404  resolver_->Start(address);
405}
406
407void TurnPort::OnResolveResult(talk_base::AsyncResolverInterface* resolver) {
408  ASSERT(resolver == resolver_);
409  if (resolver_->GetError() != 0 ||
410      !resolver_->GetResolvedAddress(ip().family(), &server_address_.address)) {
411    LOG_J(LS_WARNING, this) << "TURN host lookup received error "
412                            << resolver_->GetError();
413    OnAllocateError();
414    return;
415  }
416
417  PrepareAddress();
418}
419
420void TurnPort::OnSendStunPacket(const void* data, size_t size,
421                                StunRequest* request) {
422  if (Send(data, size, DefaultDscpValue()) < 0) {
423    LOG_J(LS_ERROR, this) << "Failed to send TURN message, err="
424                          << socket_->GetError();
425  }
426}
427
428void TurnPort::OnStunAddress(const talk_base::SocketAddress& address) {
429  // For relay, mapped address is rel-addr.
430  set_related_address(address);
431}
432
433void TurnPort::OnAllocateSuccess(const talk_base::SocketAddress& address) {
434  connected_ = true;
435  AddAddress(address,
436             socket_->GetLocalAddress(),
437             "udp",
438             RELAY_PORT_TYPE,
439             GetRelayPreference(server_address_.proto, server_address_.secure),
440             true);
441}
442
443void TurnPort::OnAllocateError() {
444  // We will send SignalPortError asynchronously as this can be sent during
445  // port initialization. This way it will not be blocking other port
446  // creation.
447  thread()->Post(this, MSG_ERROR);
448}
449
450void TurnPort::OnMessage(talk_base::Message* message) {
451  if (message->message_id == MSG_ERROR) {
452    SignalPortError(this);
453    return;
454  }
455
456  Port::OnMessage(message);
457}
458
459void TurnPort::OnAllocateRequestTimeout() {
460  OnAllocateError();
461}
462
463void TurnPort::HandleDataIndication(const char* data, size_t size) {
464  // Read in the message, and process according to RFC5766, Section 10.4.
465  talk_base::ByteBuffer buf(data, size);
466  TurnMessage msg;
467  if (!msg.Read(&buf)) {
468    LOG_J(LS_WARNING, this) << "Received invalid TURN data indication";
469    return;
470  }
471
472  // Check mandatory attributes.
473  const StunAddressAttribute* addr_attr =
474      msg.GetAddress(STUN_ATTR_XOR_PEER_ADDRESS);
475  if (!addr_attr) {
476    LOG_J(LS_WARNING, this) << "Missing STUN_ATTR_XOR_PEER_ADDRESS attribute "
477                            << "in data indication.";
478    return;
479  }
480
481  const StunByteStringAttribute* data_attr =
482      msg.GetByteString(STUN_ATTR_DATA);
483  if (!data_attr) {
484    LOG_J(LS_WARNING, this) << "Missing STUN_ATTR_DATA attribute in "
485                            << "data indication.";
486    return;
487  }
488
489  // Verify that the data came from somewhere we think we have a permission for.
490  talk_base::SocketAddress ext_addr(addr_attr->GetAddress());
491  if (!HasPermission(ext_addr.ipaddr())) {
492    LOG_J(LS_WARNING, this) << "Received TURN data indication with invalid "
493                            << "peer address, addr="
494                            << ext_addr.ToSensitiveString();
495    return;
496  }
497
498  DispatchPacket(data_attr->bytes(), data_attr->length(), ext_addr, PROTO_UDP);
499}
500
501void TurnPort::HandleChannelData(int channel_id, const char* data,
502                                 size_t size) {
503  // Read the message, and process according to RFC5766, Section 11.6.
504  //    0                   1                   2                   3
505  //    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
506  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
507  //   |         Channel Number        |            Length             |
508  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
509  //   |                                                               |
510  //   /                       Application Data                        /
511  //   /                                                               /
512  //   |                                                               |
513  //   |                               +-------------------------------+
514  //   |                               |
515  //   +-------------------------------+
516
517  // Extract header fields from the message.
518  uint16 len = talk_base::GetBE16(data + 2);
519  if (len > size - TURN_CHANNEL_HEADER_SIZE) {
520    LOG_J(LS_WARNING, this) << "Received TURN channel data message with "
521                            << "incorrect length, len=" << len;
522    return;
523  }
524  // Allowing messages larger than |len|, as ChannelData can be padded.
525
526  TurnEntry* entry = FindEntry(channel_id);
527  if (!entry) {
528    LOG_J(LS_WARNING, this) << "Received TURN channel data message for invalid "
529                            << "channel, channel_id=" << channel_id;
530    return;
531  }
532
533  DispatchPacket(data + TURN_CHANNEL_HEADER_SIZE, len, entry->address(),
534                 PROTO_UDP);
535}
536
537void TurnPort::DispatchPacket(const char* data, size_t size,
538    const talk_base::SocketAddress& remote_addr, ProtocolType proto) {
539  if (Connection* conn = GetConnection(remote_addr)) {
540    conn->OnReadPacket(data, size);
541  } else {
542    Port::OnReadPacket(data, size, remote_addr, proto);
543  }
544}
545
546bool TurnPort::ScheduleRefresh(int lifetime) {
547  // Lifetime is in seconds; we schedule a refresh for one minute less.
548  if (lifetime < 2 * 60) {
549    LOG_J(LS_WARNING, this) << "Received response with lifetime that was "
550                            << "too short, lifetime=" << lifetime;
551    return false;
552  }
553
554  SendRequest(new TurnRefreshRequest(this), (lifetime - 60) * 1000);
555  return true;
556}
557
558void TurnPort::SendRequest(StunRequest* req, int delay) {
559  request_manager_.SendDelayed(req, delay);
560}
561
562void TurnPort::AddRequestAuthInfo(StunMessage* msg) {
563  // If we've gotten the necessary data from the server, add it to our request.
564  VERIFY(!hash_.empty());
565  VERIFY(msg->AddAttribute(new StunByteStringAttribute(
566      STUN_ATTR_USERNAME, credentials_.username)));
567  VERIFY(msg->AddAttribute(new StunByteStringAttribute(
568      STUN_ATTR_REALM, realm_)));
569  VERIFY(msg->AddAttribute(new StunByteStringAttribute(
570      STUN_ATTR_NONCE, nonce_)));
571  VERIFY(msg->AddMessageIntegrity(hash()));
572}
573
574int TurnPort::Send(const void* data, size_t len,
575                   talk_base::DiffServCodePoint dscp) {
576  return socket_->SendTo(data, len, server_address_.address, dscp);
577}
578
579void TurnPort::UpdateHash() {
580  VERIFY(ComputeStunCredentialHash(credentials_.username, realm_,
581                                   credentials_.password, &hash_));
582}
583
584bool TurnPort::UpdateNonce(StunMessage* response) {
585  // When stale nonce error received, we should update
586  // hash and store realm and nonce.
587  // Check the mandatory attributes.
588  const StunByteStringAttribute* realm_attr =
589      response->GetByteString(STUN_ATTR_REALM);
590  if (!realm_attr) {
591    LOG(LS_ERROR) << "Missing STUN_ATTR_REALM attribute in "
592                  << "stale nonce error response.";
593    return false;
594  }
595  set_realm(realm_attr->GetString());
596
597  const StunByteStringAttribute* nonce_attr =
598      response->GetByteString(STUN_ATTR_NONCE);
599  if (!nonce_attr) {
600    LOG(LS_ERROR) << "Missing STUN_ATTR_NONCE attribute in "
601                  << "stale nonce error response.";
602    return false;
603  }
604  set_nonce(nonce_attr->GetString());
605  return true;
606}
607
608static bool MatchesIP(TurnEntry* e, talk_base::IPAddress ipaddr) {
609  return e->address().ipaddr() == ipaddr;
610}
611bool TurnPort::HasPermission(const talk_base::IPAddress& ipaddr) const {
612  return (std::find_if(entries_.begin(), entries_.end(),
613      std::bind2nd(std::ptr_fun(MatchesIP), ipaddr)) != entries_.end());
614}
615
616static bool MatchesAddress(TurnEntry* e, talk_base::SocketAddress addr) {
617  return e->address() == addr;
618}
619TurnEntry* TurnPort::FindEntry(const talk_base::SocketAddress& addr) const {
620  EntryList::const_iterator it = std::find_if(entries_.begin(), entries_.end(),
621      std::bind2nd(std::ptr_fun(MatchesAddress), addr));
622  return (it != entries_.end()) ? *it : NULL;
623}
624
625static bool MatchesChannelId(TurnEntry* e, int id) {
626  return e->channel_id() == id;
627}
628TurnEntry* TurnPort::FindEntry(int channel_id) const {
629  EntryList::const_iterator it = std::find_if(entries_.begin(), entries_.end(),
630      std::bind2nd(std::ptr_fun(MatchesChannelId), channel_id));
631  return (it != entries_.end()) ? *it : NULL;
632}
633
634TurnEntry* TurnPort::CreateEntry(const talk_base::SocketAddress& addr) {
635  ASSERT(FindEntry(addr) == NULL);
636  TurnEntry* entry = new TurnEntry(this, next_channel_number_++, addr);
637  entries_.push_back(entry);
638  return entry;
639}
640
641void TurnPort::DestroyEntry(const talk_base::SocketAddress& addr) {
642  TurnEntry* entry = FindEntry(addr);
643  ASSERT(entry != NULL);
644  entry->SignalDestroyed(entry);
645  entries_.remove(entry);
646  delete entry;
647}
648
649void TurnPort::OnConnectionDestroyed(Connection* conn) {
650  // Destroying TurnEntry for the connection, which is already destroyed.
651  DestroyEntry(conn->remote_candidate().address());
652}
653
654TurnAllocateRequest::TurnAllocateRequest(TurnPort* port)
655    : StunRequest(new TurnMessage()),
656      port_(port) {
657}
658
659void TurnAllocateRequest::Prepare(StunMessage* request) {
660  // Create the request as indicated in RFC 5766, Section 6.1.
661  request->SetType(TURN_ALLOCATE_REQUEST);
662  StunUInt32Attribute* transport_attr = StunAttribute::CreateUInt32(
663      STUN_ATTR_REQUESTED_TRANSPORT);
664  transport_attr->SetValue(IPPROTO_UDP << 24);
665  VERIFY(request->AddAttribute(transport_attr));
666  if (!port_->hash().empty()) {
667    port_->AddRequestAuthInfo(request);
668  }
669}
670
671void TurnAllocateRequest::OnResponse(StunMessage* response) {
672  // Check mandatory attributes as indicated in RFC5766, Section 6.3.
673  const StunAddressAttribute* mapped_attr =
674      response->GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
675  if (!mapped_attr) {
676    LOG_J(LS_WARNING, port_) << "Missing STUN_ATTR_XOR_MAPPED_ADDRESS "
677                             << "attribute in allocate success response";
678    return;
679  }
680
681  // TODO(mallinath) - Use mapped address for STUN candidate.
682  port_->OnStunAddress(mapped_attr->GetAddress());
683
684  const StunAddressAttribute* relayed_attr =
685      response->GetAddress(STUN_ATTR_XOR_RELAYED_ADDRESS);
686  if (!relayed_attr) {
687    LOG_J(LS_WARNING, port_) << "Missing STUN_ATTR_XOR_RELAYED_ADDRESS "
688                             << "attribute in allocate success response";
689    return;
690  }
691
692  const StunUInt32Attribute* lifetime_attr =
693      response->GetUInt32(STUN_ATTR_TURN_LIFETIME);
694  if (!lifetime_attr) {
695    LOG_J(LS_WARNING, port_) << "Missing STUN_ATTR_TURN_LIFETIME attribute in "
696                             << "allocate success response";
697    return;
698  }
699  // Notify the port the allocate succeeded, and schedule a refresh request.
700  port_->OnAllocateSuccess(relayed_attr->GetAddress());
701  port_->ScheduleRefresh(lifetime_attr->value());
702}
703
704void TurnAllocateRequest::OnErrorResponse(StunMessage* response) {
705  // Process error response according to RFC5766, Section 6.4.
706  const StunErrorCodeAttribute* error_code = response->GetErrorCode();
707  switch (error_code->code()) {
708    case STUN_ERROR_UNAUTHORIZED:       // Unauthrorized.
709      OnAuthChallenge(response, error_code->code());
710      break;
711    default:
712      LOG_J(LS_WARNING, port_) << "Allocate response error, code="
713                               << error_code->code();
714      port_->OnAllocateError();
715  }
716}
717
718void TurnAllocateRequest::OnTimeout() {
719  LOG_J(LS_WARNING, port_) << "Allocate request timeout";
720  port_->OnAllocateRequestTimeout();
721}
722
723void TurnAllocateRequest::OnAuthChallenge(StunMessage* response, int code) {
724  // If we failed to authenticate even after we sent our credentials, fail hard.
725  if (code == STUN_ERROR_UNAUTHORIZED && !port_->hash().empty()) {
726    LOG_J(LS_WARNING, port_) << "Failed to authenticate with the server "
727                             << "after challenge.";
728    port_->OnAllocateError();
729    return;
730  }
731
732  // Check the mandatory attributes.
733  const StunByteStringAttribute* realm_attr =
734      response->GetByteString(STUN_ATTR_REALM);
735  if (!realm_attr) {
736    LOG_J(LS_WARNING, port_) << "Missing STUN_ATTR_REALM attribute in "
737                             << "allocate unauthorized response.";
738    return;
739  }
740  port_->set_realm(realm_attr->GetString());
741
742  const StunByteStringAttribute* nonce_attr =
743      response->GetByteString(STUN_ATTR_NONCE);
744  if (!nonce_attr) {
745    LOG_J(LS_WARNING, port_) << "Missing STUN_ATTR_NONCE attribute in "
746                             << "allocate unauthorized response.";
747    return;
748  }
749  port_->set_nonce(nonce_attr->GetString());
750
751  // Send another allocate request, with the received realm and nonce values.
752  port_->SendRequest(new TurnAllocateRequest(port_), 0);
753}
754
755TurnRefreshRequest::TurnRefreshRequest(TurnPort* port)
756    : StunRequest(new TurnMessage()),
757      port_(port) {
758}
759
760void TurnRefreshRequest::Prepare(StunMessage* request) {
761  // Create the request as indicated in RFC 5766, Section 7.1.
762  // No attributes need to be included.
763  request->SetType(TURN_REFRESH_REQUEST);
764  port_->AddRequestAuthInfo(request);
765}
766
767void TurnRefreshRequest::OnResponse(StunMessage* response) {
768  // Check mandatory attributes as indicated in RFC5766, Section 7.3.
769  const StunUInt32Attribute* lifetime_attr =
770      response->GetUInt32(STUN_ATTR_TURN_LIFETIME);
771  if (!lifetime_attr) {
772    LOG_J(LS_WARNING, port_) << "Missing STUN_ATTR_TURN_LIFETIME attribute in "
773                             << "refresh success response.";
774    return;
775  }
776
777  // Schedule a refresh based on the returned lifetime value.
778  port_->ScheduleRefresh(lifetime_attr->value());
779}
780
781void TurnRefreshRequest::OnErrorResponse(StunMessage* response) {
782  // TODO(juberti): Handle 437 error response as a success.
783  const StunErrorCodeAttribute* error_code = response->GetErrorCode();
784  LOG_J(LS_WARNING, port_) << "Refresh response error, code="
785                           << error_code->code();
786
787  if (error_code->code() == STUN_ERROR_STALE_NONCE) {
788    if (port_->UpdateNonce(response)) {
789      // Send RefreshRequest immediately.
790      port_->SendRequest(new TurnRefreshRequest(port_), 0);
791    }
792  }
793}
794
795void TurnRefreshRequest::OnTimeout() {
796}
797
798TurnCreatePermissionRequest::TurnCreatePermissionRequest(
799    TurnPort* port, TurnEntry* entry,
800    const talk_base::SocketAddress& ext_addr)
801    : StunRequest(new TurnMessage()),
802      port_(port),
803      entry_(entry),
804      ext_addr_(ext_addr) {
805  entry_->SignalDestroyed.connect(
806      this, &TurnCreatePermissionRequest::OnEntryDestroyed);
807}
808
809void TurnCreatePermissionRequest::Prepare(StunMessage* request) {
810  // Create the request as indicated in RFC5766, Section 9.1.
811  request->SetType(TURN_CREATE_PERMISSION_REQUEST);
812  VERIFY(request->AddAttribute(new StunXorAddressAttribute(
813      STUN_ATTR_XOR_PEER_ADDRESS, ext_addr_)));
814  port_->AddRequestAuthInfo(request);
815}
816
817void TurnCreatePermissionRequest::OnResponse(StunMessage* response) {
818  if (entry_) {
819    entry_->OnCreatePermissionSuccess();
820  }
821}
822
823void TurnCreatePermissionRequest::OnErrorResponse(StunMessage* response) {
824  if (entry_) {
825    const StunErrorCodeAttribute* error_code = response->GetErrorCode();
826    entry_->OnCreatePermissionError(response, error_code->code());
827  }
828}
829
830void TurnCreatePermissionRequest::OnTimeout() {
831  LOG_J(LS_WARNING, port_) << "Create permission timeout";
832}
833
834void TurnCreatePermissionRequest::OnEntryDestroyed(TurnEntry* entry) {
835  ASSERT(entry_ == entry);
836  entry_ = NULL;
837}
838
839TurnChannelBindRequest::TurnChannelBindRequest(
840    TurnPort* port, TurnEntry* entry,
841    int channel_id, const talk_base::SocketAddress& ext_addr)
842    : StunRequest(new TurnMessage()),
843      port_(port),
844      entry_(entry),
845      channel_id_(channel_id),
846      ext_addr_(ext_addr) {
847  entry_->SignalDestroyed.connect(
848      this, &TurnChannelBindRequest::OnEntryDestroyed);
849}
850
851void TurnChannelBindRequest::Prepare(StunMessage* request) {
852  // Create the request as indicated in RFC5766, Section 11.1.
853  request->SetType(TURN_CHANNEL_BIND_REQUEST);
854  VERIFY(request->AddAttribute(new StunUInt32Attribute(
855      STUN_ATTR_CHANNEL_NUMBER, channel_id_ << 16)));
856  VERIFY(request->AddAttribute(new StunXorAddressAttribute(
857      STUN_ATTR_XOR_PEER_ADDRESS, ext_addr_)));
858  port_->AddRequestAuthInfo(request);
859}
860
861void TurnChannelBindRequest::OnResponse(StunMessage* response) {
862  if (entry_) {
863    entry_->OnChannelBindSuccess();
864    // Refresh the channel binding just under the permission timeout
865    // threshold. The channel binding has a longer lifetime, but
866    // this is the easiest way to keep both the channel and the
867    // permission from expiring.
868    entry_->SendChannelBindRequest(TURN_PERMISSION_TIMEOUT - 60 * 1000);
869  }
870}
871
872void TurnChannelBindRequest::OnErrorResponse(StunMessage* response) {
873  if (entry_) {
874    const StunErrorCodeAttribute* error_code = response->GetErrorCode();
875    entry_->OnChannelBindError(response, error_code->code());
876  }
877}
878
879void TurnChannelBindRequest::OnTimeout() {
880  LOG_J(LS_WARNING, port_) << "Channel bind timeout";
881}
882
883void TurnChannelBindRequest::OnEntryDestroyed(TurnEntry* entry) {
884  ASSERT(entry_ == entry);
885  entry_ = NULL;
886}
887
888TurnEntry::TurnEntry(TurnPort* port, int channel_id,
889                     const talk_base::SocketAddress& ext_addr)
890    : port_(port),
891      channel_id_(channel_id),
892      ext_addr_(ext_addr),
893      state_(STATE_UNBOUND) {
894  // Creating permission for |ext_addr_|.
895  SendCreatePermissionRequest();
896}
897
898void TurnEntry::SendCreatePermissionRequest() {
899  port_->SendRequest(new TurnCreatePermissionRequest(
900      port_, this, ext_addr_), 0);
901}
902
903void TurnEntry::SendChannelBindRequest(int delay) {
904  port_->SendRequest(new TurnChannelBindRequest(
905      port_, this, channel_id_, ext_addr_), delay);
906}
907
908int TurnEntry::Send(const void* data, size_t size, bool payload,
909                    talk_base::DiffServCodePoint dscp) {
910  talk_base::ByteBuffer buf;
911  if (state_ != STATE_BOUND) {
912    // If we haven't bound the channel yet, we have to use a Send Indication.
913    TurnMessage msg;
914    msg.SetType(TURN_SEND_INDICATION);
915    msg.SetTransactionID(
916        talk_base::CreateRandomString(kStunTransactionIdLength));
917    VERIFY(msg.AddAttribute(new StunXorAddressAttribute(
918        STUN_ATTR_XOR_PEER_ADDRESS, ext_addr_)));
919    VERIFY(msg.AddAttribute(new StunByteStringAttribute(
920        STUN_ATTR_DATA, data, size)));
921    VERIFY(msg.Write(&buf));
922
923    // If we're sending real data, request a channel bind that we can use later.
924    if (state_ == STATE_UNBOUND && payload) {
925      SendChannelBindRequest(0);
926      state_ = STATE_BINDING;
927    }
928  } else {
929    // If the channel is bound, we can send the data as a Channel Message.
930    buf.WriteUInt16(channel_id_);
931    buf.WriteUInt16(static_cast<uint16>(size));
932    buf.WriteBytes(reinterpret_cast<const char*>(data), size);
933  }
934  return port_->Send(buf.Data(), buf.Length(), dscp);
935}
936
937void TurnEntry::OnCreatePermissionSuccess() {
938  LOG_J(LS_INFO, port_) << "Create permission for "
939                        << ext_addr_.ToSensitiveString()
940                        << " succeeded";
941  // For success result code will be 0.
942  port_->SignalCreatePermissionResult(port_, ext_addr_, 0);
943}
944
945void TurnEntry::OnCreatePermissionError(StunMessage* response, int code) {
946  LOG_J(LS_WARNING, port_) << "Create permission for "
947                           << ext_addr_.ToSensitiveString()
948                           << " failed, code=" << code;
949  if (code == STUN_ERROR_STALE_NONCE) {
950    if (port_->UpdateNonce(response)) {
951      SendCreatePermissionRequest();
952    }
953  } else {
954    // Send signal with error code.
955    port_->SignalCreatePermissionResult(port_, ext_addr_, code);
956  }
957}
958
959void TurnEntry::OnChannelBindSuccess() {
960  LOG_J(LS_INFO, port_) << "Channel bind for " << ext_addr_.ToSensitiveString()
961                        << " succeeded";
962  ASSERT(state_ == STATE_BINDING || state_ == STATE_BOUND);
963  state_ = STATE_BOUND;
964}
965
966void TurnEntry::OnChannelBindError(StunMessage* response, int code) {
967  // TODO(mallinath) - Implement handling of error response for channel
968  // bind request as per http://tools.ietf.org/html/rfc5766#section-11.3
969  LOG_J(LS_WARNING, port_) << "Channel bind for "
970                           << ext_addr_.ToSensitiveString()
971                           << " failed, code=" << code;
972  if (code == STUN_ERROR_STALE_NONCE) {
973    if (port_->UpdateNonce(response)) {
974      // Send channel bind request with fresh nonce.
975      SendChannelBindRequest(0);
976    }
977  }
978}
979
980}  // namespace cricket
Note: See TracBrowser for help on using the repository browser.