root/content/browser/renderer_host/p2p/socket_dispatcher_host.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. resolver_
  2. Resolve
  3. request_id
  4. OnDone
  5. monitoring_networks_
  6. OnChannelClosing
  7. OnDestruct
  8. OnMessageReceived
  9. OnIPAddressChanged
  10. LookupSocket
  11. OnStartNetworkNotifications
  12. OnStopNetworkNotifications
  13. OnGetHostAddress
  14. OnCreateSocket
  15. OnAcceptIncomingTcpConnection
  16. OnSend
  17. OnSetOption
  18. OnDestroySocket
  19. DoGetNetworkList
  20. SendNetworkList
  21. OnAddressResolved

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/p2p/socket_dispatcher_host.h"

#include "base/bind.h"
#include "base/stl_util.h"
#include "content/browser/renderer_host/p2p/socket_host.h"
#include "content/common/p2p_messages.h"
#include "content/public/browser/resource_context.h"
#include "net/base/address_list.h"
#include "net/base/completion_callback.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/base/sys_addrinfo.h"
#include "net/dns/single_request_host_resolver.h"
#include "net/url_request/url_request_context_getter.h"

using content::BrowserMessageFilter;
using content::BrowserThread;

namespace content {

const size_t kMaximumPacketSize = 32768;

class P2PSocketDispatcherHost::DnsRequest {
 public:
  typedef base::Callback<void(const net::IPAddressList&)> DoneCallback;

  DnsRequest(int32 request_id, net::HostResolver* host_resolver)
      : request_id_(request_id),
        resolver_(host_resolver) {
  }

  void Resolve(const std::string& host_name,
               const DoneCallback& done_callback) {
    DCHECK(!done_callback.is_null());

    host_name_ = host_name;
    done_callback_ = done_callback;

    // Return an error if it's an empty string.
    if (host_name_.empty()) {
      net::IPAddressList address_list;
      done_callback_.Run(address_list);
      return;
    }

    // Add period at the end to make sure that we only resolve
    // fully-qualified names.
    if (host_name_.at(host_name_.size() - 1) != '.')
      host_name_ = host_name_ + '.';

    net::HostResolver::RequestInfo info(net::HostPortPair(host_name_, 0));
    int result = resolver_.Resolve(
        info,
        net::DEFAULT_PRIORITY,
        &addresses_,
        base::Bind(&P2PSocketDispatcherHost::DnsRequest::OnDone,
                   base::Unretained(this)),
        net::BoundNetLog());
    if (result != net::ERR_IO_PENDING)
      OnDone(result);
  }

  int32 request_id() { return request_id_; }

 private:
  void OnDone(int result) {
    net::IPAddressList list;
    if (result != net::OK) {
      LOG(ERROR) << "Failed to resolve address for " << host_name_
                 << ", errorcode: " << result;
      done_callback_.Run(list);
      return;
    }

    DCHECK(!addresses_.empty());
    for (net::AddressList::iterator iter = addresses_.begin();
         iter != addresses_.end(); ++iter) {
      list.push_back(iter->address());
    }
    done_callback_.Run(list);
  }

  int32 request_id_;
  net::AddressList addresses_;

  std::string host_name_;
  net::SingleRequestHostResolver resolver_;

  DoneCallback done_callback_;
};

P2PSocketDispatcherHost::P2PSocketDispatcherHost(
    content::ResourceContext* resource_context,
    net::URLRequestContextGetter* url_context)
    : BrowserMessageFilter(P2PMsgStart),
      resource_context_(resource_context),
      url_context_(url_context),
      monitoring_networks_(false) {
}

void P2PSocketDispatcherHost::OnChannelClosing() {
  // Since the IPC channel is gone, close pending connections.
  STLDeleteContainerPairSecondPointers(sockets_.begin(), sockets_.end());
  sockets_.clear();

  STLDeleteContainerPointers(dns_requests_.begin(), dns_requests_.end());
  dns_requests_.clear();

  if (monitoring_networks_) {
    net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
    monitoring_networks_ = false;
  }
}

void P2PSocketDispatcherHost::OnDestruct() const {
  BrowserThread::DeleteOnIOThread::Destruct(this);
}

bool P2PSocketDispatcherHost::OnMessageReceived(const IPC::Message& message,
                                                bool* message_was_ok) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP_EX(P2PSocketDispatcherHost, message, *message_was_ok)
    IPC_MESSAGE_HANDLER(P2PHostMsg_StartNetworkNotifications,
                        OnStartNetworkNotifications)
    IPC_MESSAGE_HANDLER(P2PHostMsg_StopNetworkNotifications,
                        OnStopNetworkNotifications)
    IPC_MESSAGE_HANDLER(P2PHostMsg_GetHostAddress, OnGetHostAddress)
    IPC_MESSAGE_HANDLER(P2PHostMsg_CreateSocket, OnCreateSocket)
    IPC_MESSAGE_HANDLER(P2PHostMsg_AcceptIncomingTcpConnection,
                        OnAcceptIncomingTcpConnection)
    IPC_MESSAGE_HANDLER(P2PHostMsg_Send, OnSend)
    IPC_MESSAGE_HANDLER(P2PHostMsg_SetOption, OnSetOption)
    IPC_MESSAGE_HANDLER(P2PHostMsg_DestroySocket, OnDestroySocket)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP_EX()
  return handled;
}

void P2PSocketDispatcherHost::OnIPAddressChanged() {
  // Notify the renderer about changes to list of network interfaces.
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE, base::Bind(
          &P2PSocketDispatcherHost::DoGetNetworkList, this));
}

P2PSocketDispatcherHost::~P2PSocketDispatcherHost() {
  DCHECK(sockets_.empty());
  DCHECK(dns_requests_.empty());

  if (monitoring_networks_)
    net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
}

P2PSocketHost* P2PSocketDispatcherHost::LookupSocket(int socket_id) {
  SocketsMap::iterator it = sockets_.find(socket_id);
  return (it == sockets_.end()) ? NULL : it->second;
}

void P2PSocketDispatcherHost::OnStartNetworkNotifications(
    const IPC::Message& msg) {
  if (!monitoring_networks_) {
    net::NetworkChangeNotifier::AddIPAddressObserver(this);
    monitoring_networks_ = true;
  }

  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE, base::Bind(
          &P2PSocketDispatcherHost::DoGetNetworkList, this));
}

void P2PSocketDispatcherHost::OnStopNetworkNotifications(
    const IPC::Message& msg) {
  if (monitoring_networks_) {
    net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
    monitoring_networks_ = false;
  }
}

void P2PSocketDispatcherHost::OnGetHostAddress(const std::string& host_name,
                                               int32 request_id) {
  DnsRequest* request = new DnsRequest(request_id,
                                       resource_context_->GetHostResolver());
  dns_requests_.insert(request);
  request->Resolve(host_name, base::Bind(
      &P2PSocketDispatcherHost::OnAddressResolved,
      base::Unretained(this), request));
}

void P2PSocketDispatcherHost::OnCreateSocket(
    P2PSocketType type, int socket_id,
    const net::IPEndPoint& local_address,
    const P2PHostAndIPEndPoint& remote_address) {
  if (LookupSocket(socket_id)) {
    LOG(ERROR) << "Received P2PHostMsg_CreateSocket for socket "
        "that already exists.";
    return;
  }

  scoped_ptr<P2PSocketHost> socket(P2PSocketHost::Create(
      this, socket_id, type, url_context_.get(), &throttler_));

  if (!socket) {
    Send(new P2PMsg_OnError(socket_id));
    return;
  }

  if (socket->Init(local_address, remote_address)) {
    sockets_[socket_id] = socket.release();
  }
}

void P2PSocketDispatcherHost::OnAcceptIncomingTcpConnection(
    int listen_socket_id, const net::IPEndPoint& remote_address,
    int connected_socket_id) {
  P2PSocketHost* socket = LookupSocket(listen_socket_id);
  if (!socket) {
    LOG(ERROR) << "Received P2PHostMsg_AcceptIncomingTcpConnection "
        "for invalid socket_id.";
    return;
  }
  P2PSocketHost* accepted_connection =
      socket->AcceptIncomingTcpConnection(remote_address, connected_socket_id);
  if (accepted_connection) {
    sockets_[connected_socket_id] = accepted_connection;
  }
}

void P2PSocketDispatcherHost::OnSend(int socket_id,
                                     const net::IPEndPoint& socket_address,
                                     const std::vector<char>& data,
                                     const talk_base::PacketOptions& options,
                                     uint64 packet_id) {
  P2PSocketHost* socket = LookupSocket(socket_id);
  if (!socket) {
    LOG(ERROR) << "Received P2PHostMsg_Send for invalid socket_id.";
    return;
  }

  if (data.size() > kMaximumPacketSize) {
    LOG(ERROR) << "Received P2PHostMsg_Send with a packet that is too big: "
               << data.size();
    Send(new P2PMsg_OnError(socket_id));
    delete socket;
    sockets_.erase(socket_id);
    return;
  }

  socket->Send(socket_address, data, options, packet_id);
}

void P2PSocketDispatcherHost::OnSetOption(int socket_id,
                                          P2PSocketOption option,
                                          int value) {
  P2PSocketHost* socket = LookupSocket(socket_id);
  if (!socket) {
    LOG(ERROR) << "Received P2PHostMsg_SetOption for invalid socket_id.";
    return;
  }

  socket->SetOption(option, value);
}

void P2PSocketDispatcherHost::OnDestroySocket(int socket_id) {
  SocketsMap::iterator it = sockets_.find(socket_id);
  if (it != sockets_.end()) {
    delete it->second;
    sockets_.erase(it);
  } else {
    LOG(ERROR) << "Received P2PHostMsg_DestroySocket for invalid socket_id.";
  }
}

void P2PSocketDispatcherHost::DoGetNetworkList() {
  net::NetworkInterfaceList list;
  net::GetNetworkList(&list, net::EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES |
                             net::INCLUDE_ONLY_TEMP_IPV6_ADDRESS_IF_POSSIBLE);
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE, base::Bind(
          &P2PSocketDispatcherHost::SendNetworkList, this, list));
}

void P2PSocketDispatcherHost::SendNetworkList(
    const net::NetworkInterfaceList& list) {
  Send(new P2PMsg_NetworkListChanged(list));
}

void P2PSocketDispatcherHost::OnAddressResolved(
    DnsRequest* request,
    const net::IPAddressList& addresses) {
  Send(new P2PMsg_GetHostAddressResult(request->request_id(), addresses));

  dns_requests_.erase(request);
  delete request;
}

}  // namespace content

/* [<][>][^][v][top][bottom][index][help] */