| // 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 <stddef.h> |
| |
| #include <algorithm> |
| |
| #include "base/bind.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/task_scheduler/post_task.h" |
| #include "content/browser/bad_message.h" |
| #include "content/browser/renderer_host/p2p/socket_host.h" |
| #include "content/common/p2p_messages.h" |
| #include "content/public/browser/browser_thread.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/network_interfaces.h" |
| #include "net/base/sys_addrinfo.h" |
| #include "net/dns/host_resolver.h" |
| #include "net/log/net_log_source.h" |
| #include "net/log/net_log_with_source.h" |
| #include "net/socket/client_socket_factory.h" |
| #include "net/socket/datagram_client_socket.h" |
| #include "net/url_request/url_request_context_getter.h" |
| |
| using content::BrowserMessageFilter; |
| using content::BrowserThread; |
| |
| namespace content { |
| |
| namespace { |
| |
| // Used by GetDefaultLocalAddress as the target to connect to for getting the |
| // default local address. They are public DNS servers on the internet. |
| const uint8_t kPublicIPv4Host[] = {8, 8, 8, 8}; |
| const uint8_t kPublicIPv6Host[] = { |
| 0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0x88}; |
| const int kPublicPort = 53; // DNS port. |
| |
| } // namespace |
| |
| const size_t kMaximumPacketSize = 32768; |
| |
| class P2PSocketDispatcherHost::DnsRequest { |
| public: |
| typedef base::Callback<void(const net::IPAddressList&)> DoneCallback; |
| |
| DnsRequest(int32_t 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_.back() != '.') |
| 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)), |
| &request_, net::NetLogWithSource()); |
| if (result != net::ERR_IO_PENDING) |
| OnDone(result); |
| } |
| |
| int32_t 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_t request_id_; |
| net::AddressList addresses_; |
| |
| std::string host_name_; |
| net::HostResolver* resolver_; |
| std::unique_ptr<net::HostResolver::Request> request_; |
| |
| 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), |
| network_list_task_runner_(base::CreateSequencedTaskRunnerWithTraits( |
| {base::MayBlock(), base::TaskPriority::USER_VISIBLE})) {} |
| |
| void P2PSocketDispatcherHost::OnChannelClosing() { |
| // Since the IPC sender is gone, close pending connections. |
| sockets_.clear(); |
| |
| 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. |
| network_list_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&P2PSocketDispatcherHost::DoGetNetworkList, this)); |
| } |
| |
| void P2PSocketDispatcherHost::StartRtpDump( |
| bool incoming, |
| bool outgoing, |
| const RenderProcessHost::WebRtcRtpPacketCallback& packet_callback) { |
| DCHECK_CURRENTLY_ON(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_CURRENTLY_ON(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()) ? nullptr : it->second.get(); |
| } |
| |
| void P2PSocketDispatcherHost::OnStartNetworkNotifications() { |
| if (!monitoring_networks_) { |
| net::NetworkChangeNotifier::AddIPAddressObserver(this); |
| monitoring_networks_ = true; |
| } |
| |
| network_list_task_runner_->PostTask( |
| 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_t request_id) { |
| std::unique_ptr<DnsRequest> request = base::MakeUnique<DnsRequest>( |
| request_id, resource_context_->GetHostResolver()); |
| DnsRequest* request_ptr = request.get(); |
| dns_requests_.insert(std::move(request)); |
| request_ptr->Resolve(host_name, |
| base::Bind(&P2PSocketDispatcherHost::OnAddressResolved, |
| base::Unretained(this), request_ptr)); |
| } |
| |
| void P2PSocketDispatcherHost::OnCreateSocket( |
| P2PSocketType type, |
| int socket_id, |
| const net::IPEndPoint& local_address, |
| const P2PPortRange& port_range, |
| const P2PHostAndIPEndPoint& remote_address) { |
| if (port_range.min_port > port_range.max_port || |
| (port_range.min_port == 0 && port_range.max_port != 0)) { |
| bad_message::ReceivedBadMessage(this, bad_message::SDH_INVALID_PORT_RANGE); |
| return; |
| } |
| |
| if (LookupSocket(socket_id)) { |
| LOG(ERROR) << "Received P2PHostMsg_CreateSocket for socket " |
| "that already exists."; |
| return; |
| } |
| |
| std::unique_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, port_range.min_port, port_range.max_port, |
| remote_address)) { |
| sockets_[socket_id] = std::move(socket); |
| |
| 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 listen_socket_id."; |
| return; |
| } |
| if (LookupSocket(connected_socket_id) != nullptr) { |
| LOG(ERROR) << "Received P2PHostMsg_AcceptIncomingTcpConnection " |
| "for duplicated connected_socket_id."; |
| return; |
| } |
| |
| std::unique_ptr<P2PSocketHost> accepted_connection( |
| socket->AcceptIncomingTcpConnection(remote_address, connected_socket_id)); |
| if (accepted_connection) { |
| sockets_[connected_socket_id] = std::move(accepted_connection); |
| } |
| } |
| |
| void P2PSocketDispatcherHost::OnSend(int socket_id, |
| const net::IPEndPoint& socket_address, |
| const std::vector<char>& data, |
| const rtc::PacketOptions& options, |
| uint64_t 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)); |
| sockets_.erase(socket_id); // deletes the socket |
| 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()) { |
| sockets_.erase(it); // deletes the socket |
| } else { |
| LOG(ERROR) << "Received P2PHostMsg_DestroySocket for invalid socket_id."; |
| } |
| } |
| |
| void P2PSocketDispatcherHost::DoGetNetworkList() { |
| net::NetworkInterfaceList list; |
| if (!net::GetNetworkList(&list, net::EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES)) { |
| LOG(ERROR) << "GetNetworkList failed."; |
| return; |
| } |
| default_ipv4_local_address_ = GetDefaultLocalAddress(AF_INET); |
| default_ipv6_local_address_ = GetDefaultLocalAddress(AF_INET6); |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind(&P2PSocketDispatcherHost::SendNetworkList, this, list, |
| default_ipv4_local_address_, default_ipv6_local_address_)); |
| } |
| |
| void P2PSocketDispatcherHost::SendNetworkList( |
| const net::NetworkInterfaceList& list, |
| const net::IPAddress& default_ipv4_local_address, |
| const net::IPAddress& default_ipv6_local_address) { |
| Send(new P2PMsg_NetworkListChanged(list, default_ipv4_local_address, |
| default_ipv6_local_address)); |
| } |
| |
| net::IPAddress P2PSocketDispatcherHost::GetDefaultLocalAddress(int family) { |
| DCHECK(family == AF_INET || family == AF_INET6); |
| |
| // Creation and connection of a UDP socket might be janky. |
| DCHECK(network_list_task_runner_->RunsTasksInCurrentSequence()); |
| |
| std::unique_ptr<net::DatagramClientSocket> socket( |
| net::ClientSocketFactory::GetDefaultFactory()->CreateDatagramClientSocket( |
| net::DatagramSocket::DEFAULT_BIND, net::RandIntCallback(), nullptr, |
| net::NetLogSource())); |
| |
| net::IPAddress ip_address; |
| if (family == AF_INET) { |
| ip_address = net::IPAddress(kPublicIPv4Host); |
| } else { |
| ip_address = net::IPAddress(kPublicIPv6Host); |
| } |
| |
| if (socket->Connect(net::IPEndPoint(ip_address, kPublicPort)) != net::OK) { |
| return net::IPAddress(); |
| } |
| |
| net::IPEndPoint local_address; |
| if (socket->GetLocalAddress(&local_address) != net::OK) |
| return net::IPAddress(); |
| |
| return local_address.address(); |
| } |
| |
| void P2PSocketDispatcherHost::OnAddressResolved( |
| DnsRequest* request, |
| const net::IPAddressList& addresses) { |
| Send(new P2PMsg_GetHostAddressResult(request->request_id(), addresses)); |
| |
| dns_requests_.erase( |
| std::find_if(dns_requests_.begin(), dns_requests_.end(), |
| [request](const std::unique_ptr<DnsRequest>& ptr) { |
| return ptr.get() == 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 |