Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / p2p / base / stunport.cc
index b440bf4..0602c5a 100644 (file)
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "talk/p2p/base/stunport.h"
+#include "webrtc/p2p/base/stunport.h"
 
-#include "talk/base/common.h"
-#include "talk/base/logging.h"
-#include "talk/base/helpers.h"
-#include "talk/base/nethelpers.h"
-#include "talk/p2p/base/common.h"
-#include "talk/p2p/base/stun.h"
+#include "webrtc/p2p/base/common.h"
+#include "webrtc/p2p/base/portallocator.h"
+#include "webrtc/p2p/base/stun.h"
+#include "webrtc/base/common.h"
+#include "webrtc/base/helpers.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/nethelpers.h"
 
 namespace cricket {
 
@@ -45,15 +46,15 @@ const int RETRY_TIMEOUT = 50 * 1000;    // ICE says 50 secs
 class StunBindingRequest : public StunRequest {
  public:
   StunBindingRequest(UDPPort* port, bool keep_alive,
-                     const talk_base::SocketAddress& addr)
+                     const rtc::SocketAddress& addr)
     : port_(port), keep_alive_(keep_alive), server_addr_(addr) {
-    start_time_ = talk_base::Time();
+    start_time_ = rtc::Time();
   }
 
   virtual ~StunBindingRequest() {
   }
 
-  const talk_base::SocketAddress& server_addr() const { return server_addr_; }
+  const rtc::SocketAddress& server_addr() const { return server_addr_; }
 
   virtual void Prepare(StunMessage* request) {
     request->SetType(STUN_BINDING_REQUEST);
@@ -68,11 +69,11 @@ class StunBindingRequest : public StunRequest {
                addr_attr->family() != STUN_ADDRESS_IPV6) {
       LOG(LS_ERROR) << "Binding address has bad family";
     } else {
-      talk_base::SocketAddress addr(addr_attr->ipaddr(), addr_attr->port());
-      port_->OnStunBindingRequestSucceeded(addr);
+      rtc::SocketAddress addr(addr_attr->ipaddr(), addr_attr->port());
+      port_->OnStunBindingRequestSucceeded(server_addr_, addr);
     }
 
-    // We will do a keep-alive regardless of whether this request suceeds.
+    // We will do a keep-alive regardless of whether this request succeeds.
     // This should have almost no impact on network usage.
     if (keep_alive_) {
       port_->requests_.SendDelayed(
@@ -92,10 +93,10 @@ class StunBindingRequest : public StunRequest {
                  << " reason='" << attr->reason() << "'";
     }
 
-    port_->OnStunBindingOrResolveRequestFailed();
+    port_->OnStunBindingOrResolveRequestFailed(server_addr_);
 
     if (keep_alive_
-        && (talk_base::TimeSince(start_time_) <= RETRY_TIMEOUT)) {
+        && (rtc::TimeSince(start_time_) <= RETRY_TIMEOUT)) {
       port_->requests_.SendDelayed(
           new StunBindingRequest(port_, true, server_addr_),
           port_->stun_keepalive_delay());
@@ -107,10 +108,10 @@ class StunBindingRequest : public StunRequest {
       << port_->GetLocalAddress().ToSensitiveString()
       << " (" << port_->Network()->name() << ")";
 
-    port_->OnStunBindingOrResolveRequestFailed();
+    port_->OnStunBindingOrResolveRequestFailed(server_addr_);
 
     if (keep_alive_
-        && (talk_base::TimeSince(start_time_) <= RETRY_TIMEOUT)) {
+        && (rtc::TimeSince(start_time_) <= RETRY_TIMEOUT)) {
       port_->requests_.SendDelayed(
           new StunBindingRequest(port_, true, server_addr_),
           RETRY_DELAY);
@@ -120,35 +121,84 @@ class StunBindingRequest : public StunRequest {
  private:
   UDPPort* port_;
   bool keep_alive_;
-  talk_base::SocketAddress server_addr_;
+  const rtc::SocketAddress server_addr_;
   uint32 start_time_;
 };
 
-UDPPort::UDPPort(talk_base::Thread* thread,
-                 talk_base::Network* network,
-                 talk_base::AsyncPacketSocket* socket,
+UDPPort::AddressResolver::AddressResolver(
+    rtc::PacketSocketFactory* factory)
+    : socket_factory_(factory) {}
+
+UDPPort::AddressResolver::~AddressResolver() {
+  for (ResolverMap::iterator it = resolvers_.begin();
+       it != resolvers_.end(); ++it) {
+    it->second->Destroy(true);
+  }
+}
+
+void UDPPort::AddressResolver::Resolve(
+    const rtc::SocketAddress& address) {
+  if (resolvers_.find(address) != resolvers_.end())
+    return;
+
+  rtc::AsyncResolverInterface* resolver =
+      socket_factory_->CreateAsyncResolver();
+  resolvers_.insert(
+      std::pair<rtc::SocketAddress, rtc::AsyncResolverInterface*>(
+          address, resolver));
+
+  resolver->SignalDone.connect(this,
+                               &UDPPort::AddressResolver::OnResolveResult);
+
+  resolver->Start(address);
+}
+
+bool UDPPort::AddressResolver::GetResolvedAddress(
+    const rtc::SocketAddress& input,
+    int family,
+    rtc::SocketAddress* output) const {
+  ResolverMap::const_iterator it = resolvers_.find(input);
+  if (it == resolvers_.end())
+    return false;
+
+  return it->second->GetResolvedAddress(family, output);
+}
+
+void UDPPort::AddressResolver::OnResolveResult(
+    rtc::AsyncResolverInterface* resolver) {
+  for (ResolverMap::iterator it = resolvers_.begin();
+       it != resolvers_.end(); ++it) {
+    if (it->second == resolver) {
+      SignalDone(it->first, resolver->GetError());
+      return;
+    }
+  }
+}
+
+UDPPort::UDPPort(rtc::Thread* thread,
+                 rtc::PacketSocketFactory* factory,
+                 rtc::Network* network,
+                 rtc::AsyncPacketSocket* socket,
                  const std::string& username, const std::string& password)
-    : Port(thread, network, socket->GetLocalAddress().ipaddr(),
+    : Port(thread, factory, network, socket->GetLocalAddress().ipaddr(),
            username, password),
       requests_(thread),
       socket_(socket),
       error_(0),
-      resolver_(NULL),
       ready_(false),
       stun_keepalive_delay_(KEEPALIVE_DELAY) {
 }
 
-UDPPort::UDPPort(talk_base::Thread* thread,
-                   talk_base::PacketSocketFactory* factory,
-                   talk_base::Network* network,
-                   const talk_base::IPAddress& ip, int min_port, int max_port,
-                   const std::string& username, const std::string& password)
+UDPPort::UDPPort(rtc::Thread* thread,
+                 rtc::PacketSocketFactory* factory,
+                 rtc::Network* network,
+                 const rtc::IPAddress& ip, int min_port, int max_port,
+                 const std::string& username, const std::string& password)
     : Port(thread, LOCAL_PORT_TYPE, factory, network, ip, min_port, max_port,
            username, password),
       requests_(thread),
       socket_(NULL),
       error_(0),
-      resolver_(NULL),
       ready_(false),
       stun_keepalive_delay_(KEEPALIVE_DELAY) {
 }
@@ -157,7 +207,7 @@ bool UDPPort::Init() {
   if (!SharedSocket()) {
     ASSERT(socket_ == NULL);
     socket_ = socket_factory()->CreateUdpSocket(
-        talk_base::SocketAddress(ip(), 0), min_port(), max_port());
+        rtc::SocketAddress(ip(), 0), min_port(), max_port());
     if (!socket_) {
       LOG_J(LS_WARNING, this) << "UDP socket creation failed";
       return false;
@@ -171,16 +221,13 @@ bool UDPPort::Init() {
 }
 
 UDPPort::~UDPPort() {
-  if (resolver_) {
-    resolver_->Destroy(false);
-  }
   if (!SharedSocket())
     delete socket_;
 }
 
 void UDPPort::PrepareAddress() {
   ASSERT(requests_.empty());
-  if (socket_->GetState() == talk_base::AsyncPacketSocket::STATE_BOUND) {
+  if (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND) {
     OnLocalAddressReady(socket_, socket_->GetLocalAddress());
   }
 }
@@ -188,11 +235,11 @@ void UDPPort::PrepareAddress() {
 void UDPPort::MaybePrepareStunCandidate() {
   // Sending binding request to the STUN server if address is available to
   // prepare STUN candidate.
-  if (!server_addr_.IsNil()) {
-    SendStunBindingRequest();
+  if (!server_addresses_.empty()) {
+    SendStunBindingRequests();
   } else {
-    // Processing host candidate address.
-    SetResult(true);
+    // Port is done allocating candidates.
+    MaybeSetPortCompleteOrError();
   }
 }
 
@@ -216,10 +263,10 @@ Connection* UDPPort::CreateConnection(const Candidate& address,
 }
 
 int UDPPort::SendTo(const void* data, size_t size,
-                    const talk_base::SocketAddress& addr,
-                    talk_base::DiffServCodePoint dscp,
+                    const rtc::SocketAddress& addr,
+                    const rtc::PacketOptions& options,
                     bool payload) {
-  int sent = socket_->SendTo(data, size, addr, dscp);
+  int sent = socket_->SendTo(data, size, addr, options);
   if (sent < 0) {
     error_ = socket_->GetError();
     LOG_J(LS_ERROR, this) << "UDP send of " << size
@@ -228,17 +275,11 @@ int UDPPort::SendTo(const void* data, size_t size,
   return sent;
 }
 
-int UDPPort::SetOption(talk_base::Socket::Option opt, int value) {
-  // TODO(mallinath) - After we have the support on socket,
-  // remove this specialization.
-  if (opt == talk_base::Socket::OPT_DSCP) {
-    SetDefaultDscpValue(static_cast<talk_base::DiffServCodePoint>(value));
-    return 0;
-  }
+int UDPPort::SetOption(rtc::Socket::Option opt, int value) {
   return socket_->SetOption(opt, value);
 }
 
-int UDPPort::GetOption(talk_base::Socket::Option opt, int* value) {
+int UDPPort::GetOption(rtc::Socket::Option opt, int* value) {
   return socket_->GetOption(opt, value);
 }
 
@@ -246,109 +287,160 @@ int UDPPort::GetError() {
   return error_;
 }
 
-void UDPPort::OnLocalAddressReady(talk_base::AsyncPacketSocket* socket,
-                                  const talk_base::SocketAddress& address) {
-  AddAddress(address, address, UDP_PROTOCOL_NAME, LOCAL_PORT_TYPE,
-             ICE_TYPE_PREFERENCE_HOST, false);
+void UDPPort::OnLocalAddressReady(rtc::AsyncPacketSocket* socket,
+                                  const rtc::SocketAddress& address) {
+  AddAddress(address, address, rtc::SocketAddress(),
+             UDP_PROTOCOL_NAME, "", LOCAL_PORT_TYPE,
+             ICE_TYPE_PREFERENCE_HOST, 0, false);
   MaybePrepareStunCandidate();
 }
 
-void UDPPort::OnReadPacket(talk_base::AsyncPacketSocket* socket,
-                           const char* data, size_t size,
-                           const talk_base::SocketAddress& remote_addr) {
+void UDPPort::OnReadPacket(
+  rtc::AsyncPacketSocket* socket, const char* data, size_t size,
+  const rtc::SocketAddress& remote_addr,
+  const rtc::PacketTime& packet_time) {
   ASSERT(socket == socket_);
+  ASSERT(!remote_addr.IsUnresolved());
 
   // Look for a response from the STUN server.
   // Even if the response doesn't match one of our outstanding requests, we
   // will eat it because it might be a response to a retransmitted packet, and
   // we already cleared the request when we got the first response.
-  if (!server_addr_.IsUnresolved() && remote_addr == server_addr_) {
+  if (server_addresses_.find(remote_addr) != server_addresses_.end()) {
     requests_.CheckResponse(data, size);
     return;
   }
 
   if (Connection* conn = GetConnection(remote_addr)) {
-    conn->OnReadPacket(data, size);
+    conn->OnReadPacket(data, size, packet_time);
   } else {
     Port::OnReadPacket(data, size, remote_addr, PROTO_UDP);
   }
 }
 
-void UDPPort::OnReadyToSend(talk_base::AsyncPacketSocket* socket) {
+void UDPPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
   Port::OnReadyToSend();
 }
 
-void UDPPort::SendStunBindingRequest() {
+void UDPPort::SendStunBindingRequests() {
   // We will keep pinging the stun server to make sure our NAT pin-hole stays
   // open during the call.
-  // TODO: Support multiple stun servers, or make ResolveStunAddress find a
-  // server with the correct family, or something similar.
   ASSERT(requests_.empty());
-  if (server_addr_.IsUnresolved()) {
-    ResolveStunAddress();
-  } else if (socket_->GetState() == talk_base::AsyncPacketSocket::STATE_BOUND) {
-    // Check if |server_addr_| is compatible with the port's ip.
-    if (IsCompatibleAddress(server_addr_)) {
-      requests_.Send(new StunBindingRequest(this, true, server_addr_));
-    } else {
-      // Since we can't send stun messages to the server, we should mark this
-      // port ready.
-      OnStunBindingOrResolveRequestFailed();
-    }
+
+  for (ServerAddresses::const_iterator it = server_addresses_.begin();
+       it != server_addresses_.end(); ++it) {
+    SendStunBindingRequest(*it);
   }
 }
 
-void UDPPort::ResolveStunAddress() {
-  if (resolver_)
-    return;
+void UDPPort::ResolveStunAddress(const rtc::SocketAddress& stun_addr) {
+  if (!resolver_) {
+    resolver_.reset(new AddressResolver(socket_factory()));
+    resolver_->SignalDone.connect(this, &UDPPort::OnResolveResult);
+  }
 
-  resolver_ = new talk_base::AsyncResolver();
-  resolver_->SignalWorkDone.connect(this, &UDPPort::OnResolveResult);
-  resolver_->set_address(server_addr_);
-  resolver_->Start();
+  resolver_->Resolve(stun_addr);
 }
 
-void UDPPort::OnResolveResult(talk_base::SignalThread* t) {
-  ASSERT(t == resolver_);
-  if (resolver_->error() != 0) {
+void UDPPort::OnResolveResult(const rtc::SocketAddress& input,
+                              int error) {
+  ASSERT(resolver_.get() != NULL);
+
+  rtc::SocketAddress resolved;
+  if (error != 0 ||
+      !resolver_->GetResolvedAddress(input, ip().family(), &resolved))  {
     LOG_J(LS_WARNING, this) << "StunPort: stun host lookup received error "
-                            << resolver_->error();
-    OnStunBindingOrResolveRequestFailed();
+                            << error;
+    OnStunBindingOrResolveRequestFailed(input);
+    return;
   }
 
-  server_addr_ = resolver_->address();
-  SendStunBindingRequest();
+  server_addresses_.erase(input);
+
+  if (server_addresses_.find(resolved) == server_addresses_.end()) {
+    server_addresses_.insert(resolved);
+    SendStunBindingRequest(resolved);
+  }
+}
+
+void UDPPort::SendStunBindingRequest(
+    const rtc::SocketAddress& stun_addr) {
+  if (stun_addr.IsUnresolved()) {
+    ResolveStunAddress(stun_addr);
+
+  } else if (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND) {
+    // Check if |server_addr_| is compatible with the port's ip.
+    if (IsCompatibleAddress(stun_addr)) {
+      requests_.Send(new StunBindingRequest(this, true, stun_addr));
+    } else {
+      // Since we can't send stun messages to the server, we should mark this
+      // port ready.
+      LOG(LS_WARNING) << "STUN server address is incompatible.";
+      OnStunBindingOrResolveRequestFailed(stun_addr);
+    }
+  }
 }
 
 void UDPPort::OnStunBindingRequestSucceeded(
-    const talk_base::SocketAddress& stun_addr) {
-  if (ready_)  // Discarding the binding response if port is already enabled.
+    const rtc::SocketAddress& stun_server_addr,
+    const rtc::SocketAddress& stun_reflected_addr) {
+  if (bind_request_succeeded_servers_.find(stun_server_addr) !=
+          bind_request_succeeded_servers_.end()) {
     return;
+  }
+  bind_request_succeeded_servers_.insert(stun_server_addr);
+
+  // If socket is shared and |stun_reflected_addr| is equal to local socket
+  // address, or if the same address has been added by another STUN server,
+  // then discarding the stun address.
+  // For STUN, related address is the local socket address.
+  if ((!SharedSocket() || stun_reflected_addr != socket_->GetLocalAddress()) &&
+      !HasCandidateWithAddress(stun_reflected_addr)) {
+
+    rtc::SocketAddress related_address = socket_->GetLocalAddress();
+    if (!(candidate_filter() & CF_HOST)) {
+      // If candidate filter doesn't have CF_HOST specified, empty raddr to
+      // avoid local address leakage.
+      related_address = rtc::EmptySocketAddressWithFamily(
+          related_address.family());
+    }
 
-  if (!SharedSocket() || stun_addr != socket_->GetLocalAddress()) {
-    // If socket is shared and |stun_addr| is equal to local socket
-    // address then discarding the stun address.
-    // Setting related address before STUN candidate is added. For STUN
-    // related address is local socket address.
-    set_related_address(socket_->GetLocalAddress());
-    AddAddress(stun_addr, socket_->GetLocalAddress(), UDP_PROTOCOL_NAME,
-               STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_PRFLX, false);
+    AddAddress(stun_reflected_addr, socket_->GetLocalAddress(),
+               related_address, UDP_PROTOCOL_NAME, "",
+               STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, 0, false);
   }
-  SetResult(true);
+  MaybeSetPortCompleteOrError();
 }
 
-void UDPPort::OnStunBindingOrResolveRequestFailed() {
-  if (ready_)  // Discarding failure response if port is already enabled.
+void UDPPort::OnStunBindingOrResolveRequestFailed(
+    const rtc::SocketAddress& stun_server_addr) {
+  if (bind_request_failed_servers_.find(stun_server_addr) !=
+          bind_request_failed_servers_.end()) {
     return;
-
-  // If socket is shared, we should process local udp candidate.
-  SetResult(SharedSocket());
+  }
+  bind_request_failed_servers_.insert(stun_server_addr);
+  MaybeSetPortCompleteOrError();
 }
 
-void UDPPort::SetResult(bool success) {
+void UDPPort::MaybeSetPortCompleteOrError() {
+  if (ready_)
+    return;
+
+  // Do not set port ready if we are still waiting for bind responses.
+  const size_t servers_done_bind_request = bind_request_failed_servers_.size() +
+      bind_request_succeeded_servers_.size();
+  if (server_addresses_.size() != servers_done_bind_request) {
+    return;
+  }
+
   // Setting ready status.
   ready_ = true;
-  if (success) {
+
+  // The port is "completed" if there is no stun server provided, or the bind
+  // request succeeded for any stun server, or the socket is shared.
+  if (server_addresses_.empty() ||
+      bind_request_succeeded_servers_.size() > 0 ||
+      SharedSocket()) {
     SignalPortComplete(this);
   } else {
     SignalPortError(this);
@@ -358,8 +450,19 @@ void UDPPort::SetResult(bool success) {
 // TODO: merge this with SendTo above.
 void UDPPort::OnSendPacket(const void* data, size_t size, StunRequest* req) {
   StunBindingRequest* sreq = static_cast<StunBindingRequest*>(req);
-  if (socket_->SendTo(data, size, sreq->server_addr(), DefaultDscpValue()) < 0)
+  rtc::PacketOptions options(DefaultDscpValue());
+  if (socket_->SendTo(data, size, sreq->server_addr(), options) < 0)
     PLOG(LERROR, socket_->GetError()) << "sendto";
 }
 
+bool UDPPort::HasCandidateWithAddress(const rtc::SocketAddress& addr) const {
+  const std::vector<Candidate>& existing_candidates = Candidates();
+  std::vector<Candidate>::const_iterator it = existing_candidates.begin();
+  for (; it != existing_candidates.end(); ++it) {
+    if (it->address() == addr)
+      return true;
+  }
+  return false;
+}
+
 }  // namespace cricket