blob: d791db160e68f4150d52ad218382411212e37992 [file] [log] [blame]
// 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