// 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()) { 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), dump_incoming_rtp_packet_(false), dump_outgoing_rtp_packet_(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 handled = true; IPC_BEGIN_MESSAGE_MAP(P2PSocketDispatcherHost, message) 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() 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)); } void P2PSocketDispatcherHost::StartRtpDump( bool incoming, bool outgoing, const RenderProcessHost::WebRtcRtpPacketCallback& packet_callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if ((!dump_incoming_rtp_packet_ && incoming) || (!dump_outgoing_rtp_packet_ && outgoing)) { if (incoming) dump_incoming_rtp_packet_ = true; if (outgoing) dump_outgoing_rtp_packet_ = true; packet_callback_ = packet_callback; for (SocketsMap::iterator it = sockets_.begin(); it != sockets_.end(); ++it) it->second->StartRtpDump(incoming, outgoing, packet_callback); } } void P2PSocketDispatcherHost::StopRtpDumpOnUIThread(bool incoming, bool outgoing) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&P2PSocketDispatcherHost::StopRtpDumpOnIOThread, this, incoming, outgoing)); } 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() { if (!monitoring_networks_) { net::NetworkChangeNotifier::AddIPAddressObserver(this); monitoring_networks_ = true; } BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, base::Bind( &P2PSocketDispatcherHost::DoGetNetworkList, this)); } void P2PSocketDispatcherHost::OnStopNetworkNotifications() { 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 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(); if (dump_incoming_rtp_packet_ || dump_outgoing_rtp_packet_) { sockets_[socket_id]->StartRtpDump(dump_incoming_rtp_packet_, dump_outgoing_rtp_packet_, packet_callback_); } } } 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, 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; } void P2PSocketDispatcherHost::StopRtpDumpOnIOThread(bool incoming, bool outgoing) { if ((dump_incoming_rtp_packet_ && incoming) || (dump_outgoing_rtp_packet_ && outgoing)) { if (incoming) dump_incoming_rtp_packet_ = false; if (outgoing) dump_outgoing_rtp_packet_ = false; if (!dump_incoming_rtp_packet_ && !dump_outgoing_rtp_packet_) packet_callback_.Reset(); for (SocketsMap::iterator it = sockets_.begin(); it != sockets_.end(); ++it) it->second->StopRtpDump(incoming, outgoing); } } } // namespace content