blob: 79cbad177530da7a5fe806548af1bf9aec832dc2 [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 "services/network/p2p/socket.h"
#include <utility>
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/sys_byteorder.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/network/p2p/socket_tcp.h"
#include "services/network/p2p/socket_tcp_server.h"
#include "services/network/p2p/socket_udp.h"
#include "services/network/proxy_resolving_client_socket_factory.h"
namespace {
// Used to back histogram value of "WebRTC.ICE.TcpSocketErrorCode" and
// "WebRTC.ICE.UdpSocketErrorCode".
enum class SocketErrorCode {
ERR_MSG_TOO_BIG,
ERR_ADDRESS_UNREACHABLE,
ERR_ADDRESS_INVALID,
ERR_INTERNET_DISCONNECTED,
ERR_TIMED_OUT,
ERR_INSUFFICIENT_RESOURCES,
ERR_OUT_OF_MEMORY,
ERR_OTHER // For all the others
};
const uint32_t kStunMagicCookie = 0x2112A442;
// Map the network error to SocketErrorCode for the UMA histogram.
// static
static SocketErrorCode MapNetErrorToSocketErrorCode(int net_err) {
switch (net_err) {
case net::OK:
NOTREACHED();
return SocketErrorCode::ERR_OTHER;
case net::ERR_MSG_TOO_BIG:
return SocketErrorCode::ERR_MSG_TOO_BIG;
case net::ERR_ADDRESS_UNREACHABLE:
return SocketErrorCode::ERR_ADDRESS_UNREACHABLE;
case net::ERR_ADDRESS_INVALID:
return SocketErrorCode::ERR_ADDRESS_INVALID;
case net::ERR_INTERNET_DISCONNECTED:
return SocketErrorCode::ERR_INTERNET_DISCONNECTED;
case net::ERR_TIMED_OUT:
return SocketErrorCode::ERR_TIMED_OUT;
case net::ERR_INSUFFICIENT_RESOURCES:
return SocketErrorCode::ERR_INSUFFICIENT_RESOURCES;
case net::ERR_OUT_OF_MEMORY:
return SocketErrorCode::ERR_OUT_OF_MEMORY;
default:
return SocketErrorCode::ERR_OTHER;
}
}
} // namespace
namespace network {
P2PSocket::P2PSocket(Delegate* delegate,
mojom::P2PSocketClientPtr client,
mojom::P2PSocketRequest socket,
ProtocolType protocol_type)
: delegate_(delegate),
client_(std::move(client)),
binding_(this, std::move(socket)),
protocol_type_(protocol_type),
weak_ptr_factory_(this) {
binding_.set_connection_error_handler(
base::BindOnce(&P2PSocket::OnError, base::Unretained(this)));
}
P2PSocket::~P2PSocket() {
if (protocol_type_ == P2PSocket::UDP) {
UMA_HISTOGRAM_COUNTS_10000("WebRTC.SystemMaxConsecutiveBytesDelayed_UDP",
send_bytes_delayed_max_);
} else {
UMA_HISTOGRAM_COUNTS_10000("WebRTC.SystemMaxConsecutiveBytesDelayed_TCP",
send_bytes_delayed_max_);
}
if (send_packets_total_ > 0) {
int delay_rate = (send_packets_delayed_total_ * 100) / send_packets_total_;
if (protocol_type_ == P2PSocket::UDP) {
UMA_HISTOGRAM_PERCENTAGE("WebRTC.SystemPercentPacketsDelayed_UDP",
delay_rate);
} else {
UMA_HISTOGRAM_PERCENTAGE("WebRTC.SystemPercentPacketsDelayed_TCP",
delay_rate);
}
}
}
// Verifies that the packet |data| has a valid STUN header.
// static
bool P2PSocket::GetStunPacketType(const uint8_t* data,
int data_size,
StunMessageType* type) {
if (data_size < kStunHeaderSize) {
return false;
}
uint32_t cookie =
base::NetToHost32(*reinterpret_cast<const uint32_t*>(data + 4));
if (cookie != kStunMagicCookie) {
return false;
}
uint16_t length =
base::NetToHost16(*reinterpret_cast<const uint16_t*>(data + 2));
if (length != data_size - kStunHeaderSize) {
return false;
}
int message_type =
base::NetToHost16(*reinterpret_cast<const uint16_t*>(data));
// Verify that the type is known:
switch (message_type) {
case STUN_BINDING_REQUEST:
case STUN_BINDING_RESPONSE:
case STUN_BINDING_ERROR_RESPONSE:
case STUN_SHARED_SECRET_REQUEST:
case STUN_SHARED_SECRET_RESPONSE:
case STUN_SHARED_SECRET_ERROR_RESPONSE:
case STUN_ALLOCATE_REQUEST:
case STUN_ALLOCATE_RESPONSE:
case STUN_ALLOCATE_ERROR_RESPONSE:
case STUN_SEND_REQUEST:
case STUN_SEND_RESPONSE:
case STUN_SEND_ERROR_RESPONSE:
case STUN_DATA_INDICATION:
*type = static_cast<StunMessageType>(message_type);
return true;
default:
return false;
}
}
// static
bool P2PSocket::IsRequestOrResponse(StunMessageType type) {
return type == STUN_BINDING_REQUEST || type == STUN_BINDING_RESPONSE ||
type == STUN_ALLOCATE_REQUEST || type == STUN_ALLOCATE_RESPONSE;
}
// static
void P2PSocket::ReportSocketError(int result, const char* histogram_name) {
SocketErrorCode error_code = MapNetErrorToSocketErrorCode(result);
UMA_HISTOGRAM_ENUMERATION(histogram_name, static_cast<int>(error_code),
static_cast<int>(SocketErrorCode::ERR_OTHER) + 1);
}
// static
std::unique_ptr<P2PSocket> P2PSocket::Create(
Delegate* delegate,
mojom::P2PSocketClientPtr client,
mojom::P2PSocketRequest socket,
P2PSocketType type,
net::NetLog* net_log,
ProxyResolvingClientSocketFactory* proxy_resolving_socket_factory,
P2PMessageThrottler* throttler) {
switch (type) {
case P2P_SOCKET_UDP:
return std::make_unique<P2PSocketUdp>(
delegate, std::move(client), std::move(socket), throttler, net_log);
case P2P_SOCKET_TCP_SERVER:
return std::make_unique<P2PSocketTcpServer>(delegate, std::move(client),
std::move(socket),
P2P_SOCKET_TCP_CLIENT);
case P2P_SOCKET_STUN_TCP_SERVER:
return std::make_unique<P2PSocketTcpServer>(delegate, std::move(client),
std::move(socket),
P2P_SOCKET_STUN_TCP_CLIENT);
case P2P_SOCKET_TCP_CLIENT:
case P2P_SOCKET_SSLTCP_CLIENT:
case P2P_SOCKET_TLS_CLIENT:
return std::make_unique<P2PSocketTcp>(delegate, std::move(client),
std::move(socket), type,
proxy_resolving_socket_factory);
case P2P_SOCKET_STUN_TCP_CLIENT:
case P2P_SOCKET_STUN_SSLTCP_CLIENT:
case P2P_SOCKET_STUN_TLS_CLIENT:
return std::make_unique<P2PSocketStunTcp>(delegate, std::move(client),
std::move(socket), type,
proxy_resolving_socket_factory);
}
NOTREACHED();
return nullptr;
}
mojom::P2PSocketClientPtr P2PSocket::ReleaseClientForTesting() {
return std::move(client_);
}
mojom::P2PSocketRequest P2PSocket::ReleaseBindingForTesting() {
return binding_.Unbind();
}
void P2PSocket::IncrementDelayedPackets() {
send_packets_delayed_total_++;
}
void P2PSocket::IncrementTotalSentPackets() {
send_packets_total_++;
}
void P2PSocket::IncrementDelayedBytes(uint32_t size) {
send_bytes_delayed_cur_ += size;
if (send_bytes_delayed_cur_ > send_bytes_delayed_max_) {
send_bytes_delayed_max_ = send_bytes_delayed_cur_;
}
}
void P2PSocket::DecrementDelayedBytes(uint32_t size) {
send_bytes_delayed_cur_ -= size;
DCHECK_GE(send_bytes_delayed_cur_, 0);
}
void P2PSocket::OnError() {
binding_.Close();
client_.reset();
delegate_->DestroySocket(this);
}
} // namespace network