// 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 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()) { done_callback_.Run(net::IPAddressNumber()); 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) { if (result != net::OK) { LOG(ERROR) << "Failed to resolve address for " << host_name_ << ", errorcode: " << result; done_callback_.Run(net::IPAddressNumber()); return; } DCHECK(!addresses_.empty()); done_callback_.Run(addresses_.front().address()); } 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) : 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_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 net::IPEndPoint& remote_address) { if (LookupSocket(socket_id)) { LOG(ERROR) << "Received P2PHostMsg_CreateSocket for socket " "that already exists."; return; } scoped_ptr 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& data, net::DiffServCodePoint dscp, 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, dscp, packet_id); } 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); 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::IPAddressNumber& result) { Send(new P2PMsg_GetHostAddressResult(request->request_id(), result)); dns_requests_.erase(request); delete request; } } // namespace content