910e62b5创建于 1月15日历史提交
// Copyright 2012 The Chromium Authors
// 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/containers/span_reader.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_macros.h"
#include "net/base/net_errors.h"
#include "services/network/p2p/socket_tcp.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();
    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,
                     mojo::PendingRemote<mojom::P2PSocketClient> client,
                     mojo::PendingReceiver<mojom::P2PSocket> socket,
                     ProtocolType protocol_type)
    : delegate_(delegate),
      client_(std::move(client)),
      receiver_(this, std::move(socket)),
      protocol_type_(protocol_type) {
  receiver_.set_disconnect_handler(
      base::BindOnce(&P2PSocket::OnError, base::Unretained(this)));
}

P2PSocket::~P2PSocket() = default;

// Verifies that the packet |data| has a valid STUN header.
// static
bool P2PSocket::GetStunPacketType(base::span<const uint8_t> data,
                                  StunMessageType* type) {
  // See https://www.rfc-editor.org/rfc/rfc5389.html#section-6
  auto reader = base::SpanReader(data);
  uint16_t message_type, length;
  uint32_t cookie;
  if (data.size() < kStunHeaderSize ||            //
      !reader.ReadU16BigEndian(message_type) ||   //
      !reader.ReadU16BigEndian(length) ||         //
      length != data.size() - kStunHeaderSize ||  //
      !reader.ReadU32BigEndian(cookie) ||         //
      cookie != kStunMagicCookie) {
    return false;
  }

  // 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,
    mojo::PendingRemote<mojom::P2PSocketClient> client,
    mojo::PendingReceiver<mojom::P2PSocket> socket,
    P2PSocketType type,
    const net::NetworkTrafficAnnotationTag& traffic_annotation,
    net::NetLog* net_log,
    ProxyResolvingClientSocketFactory* proxy_resolving_socket_factory,
    P2PMessageThrottler* throttler,
    std::optional<base::UnguessableToken> devtools_token) {
  switch (type) {
    case P2P_SOCKET_UDP:
      return std::make_unique<P2PSocketUdp>(
          delegate, std::move(client), std::move(socket), throttler,
          traffic_annotation, net_log, devtools_token);
    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,
          traffic_annotation, 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,
          traffic_annotation, proxy_resolving_socket_factory);
  }

  NOTREACHED();
}

mojo::PendingRemote<mojom::P2PSocketClient>
P2PSocket::ReleaseClientForTesting() {
  return client_.Unbind();
}

mojo::PendingReceiver<mojom::P2PSocket> P2PSocket::ReleaseReceiverForTesting() {
  return receiver_.Unbind();
}

void P2PSocket::OnError() {
  receiver_.reset();
  client_.reset();
  delegate_->DestroySocket(this);
}

}  // namespace network