Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
8
TMessagesProj/jni/voip/webrtc/p2p/OWNERS
Normal file
8
TMessagesProj/jni/voip/webrtc/p2p/OWNERS
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
hta@webrtc.org
|
||||
mflodman@webrtc.org
|
||||
perkj@webrtc.org
|
||||
qingsi@webrtc.org
|
||||
sergeyu@chromium.org
|
||||
tommi@webrtc.org
|
||||
deadbeef@webrtc.org
|
||||
jonaso@webrtc.org
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2022 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_ACTIVE_ICE_CONTROLLER_FACTORY_INTERFACE_H_
|
||||
#define P2P_BASE_ACTIVE_ICE_CONTROLLER_FACTORY_INTERFACE_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "p2p/base/active_ice_controller_interface.h"
|
||||
#include "p2p/base/ice_agent_interface.h"
|
||||
#include "p2p/base/ice_controller_factory_interface.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// An active ICE controller may be constructed with the same arguments as a
|
||||
// legacy ICE controller. Additionally, an ICE agent must be provided for the
|
||||
// active ICE controller to interact with.
|
||||
struct ActiveIceControllerFactoryArgs {
|
||||
IceControllerFactoryArgs legacy_args;
|
||||
IceAgentInterface* ice_agent;
|
||||
};
|
||||
|
||||
class ActiveIceControllerFactoryInterface {
|
||||
public:
|
||||
virtual ~ActiveIceControllerFactoryInterface() = default;
|
||||
virtual std::unique_ptr<ActiveIceControllerInterface> Create(
|
||||
const ActiveIceControllerFactoryArgs&) = 0;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_ACTIVE_ICE_CONTROLLER_FACTORY_INTERFACE_H_
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright 2022 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_ACTIVE_ICE_CONTROLLER_INTERFACE_H_
|
||||
#define P2P_BASE_ACTIVE_ICE_CONTROLLER_INTERFACE_H_
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/array_view.h"
|
||||
#include "p2p/base/connection.h"
|
||||
#include "p2p/base/ice_switch_reason.h"
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// ActiveIceControllerInterface defines the methods for a module that actively
|
||||
// manages the connection used by an ICE transport.
|
||||
//
|
||||
// An active ICE controller receives updates from the ICE transport when
|
||||
// - the connections state is mutated
|
||||
// - a new connection should be selected as a result of an external event (eg.
|
||||
// a different connection nominated by the remote peer)
|
||||
//
|
||||
// The active ICE controller takes the appropriate decisions and requests the
|
||||
// ICE agent to perform the necessary actions through the IceAgentInterface.
|
||||
class ActiveIceControllerInterface {
|
||||
public:
|
||||
virtual ~ActiveIceControllerInterface() = default;
|
||||
|
||||
// Sets the current ICE configuration.
|
||||
virtual void SetIceConfig(const IceConfig& config) = 0;
|
||||
|
||||
// Called when a new connection is added to the ICE transport.
|
||||
virtual void OnConnectionAdded(const Connection* connection) = 0;
|
||||
|
||||
// Called when the transport switches that connection in active use.
|
||||
virtual void OnConnectionSwitched(const Connection* connection) = 0;
|
||||
|
||||
// Called when a connection is destroyed.
|
||||
virtual void OnConnectionDestroyed(const Connection* connection) = 0;
|
||||
|
||||
// Called when a STUN ping has been sent on a connection. This does not
|
||||
// indicate that a STUN response has been received.
|
||||
virtual void OnConnectionPinged(const Connection* connection) = 0;
|
||||
|
||||
// Called when one of the following changes for a connection.
|
||||
// - rtt estimate
|
||||
// - write state
|
||||
// - receiving
|
||||
// - connected
|
||||
// - nominated
|
||||
virtual void OnConnectionUpdated(const Connection* connection) = 0;
|
||||
|
||||
// Compute "STUN_ATTR_USE_CANDIDATE" for a STUN ping on the given connection.
|
||||
virtual bool GetUseCandidateAttribute(const Connection* connection,
|
||||
NominationMode mode,
|
||||
IceMode remote_ice_mode) const = 0;
|
||||
|
||||
// Called to enque a request to pick and switch to the best available
|
||||
// connection.
|
||||
virtual void OnSortAndSwitchRequest(IceSwitchReason reason) = 0;
|
||||
|
||||
// Called to pick and switch to the best available connection immediately.
|
||||
virtual void OnImmediateSortAndSwitchRequest(IceSwitchReason reason) = 0;
|
||||
|
||||
// Called to switch to the given connection immediately without checking for
|
||||
// the best available connection.
|
||||
virtual bool OnImmediateSwitchRequest(IceSwitchReason reason,
|
||||
const Connection* selected) = 0;
|
||||
|
||||
// Only for unit tests
|
||||
virtual const Connection* FindNextPingableConnection() = 0;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_ACTIVE_ICE_CONTROLLER_INTERFACE_H_
|
||||
162
TMessagesProj/jni/voip/webrtc/p2p/base/async_stun_tcp_socket.cc
Normal file
162
TMessagesProj/jni/voip/webrtc/p2p/base/async_stun_tcp_socket.cc
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright 2013 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/async_stun_tcp_socket.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "api/transport/stun.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "rtc_base/byte_order.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/network/received_packet.h"
|
||||
#include "rtc_base/network/sent_packet.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
static const size_t kMaxPacketSize = 64 * 1024;
|
||||
|
||||
typedef uint16_t PacketLength;
|
||||
static const size_t kPacketLenSize = sizeof(PacketLength);
|
||||
static const size_t kPacketLenOffset = 2;
|
||||
static const size_t kBufSize = kMaxPacketSize + kStunHeaderSize;
|
||||
static const size_t kTurnChannelDataHdrSize = 4;
|
||||
|
||||
inline bool IsStunMessage(uint16_t msg_type) {
|
||||
// The first two bits of a channel data message are 0b01.
|
||||
return (msg_type & 0xC000) ? false : true;
|
||||
}
|
||||
|
||||
// AsyncStunTCPSocket
|
||||
// Binds and connects `socket` and creates AsyncTCPSocket for
|
||||
// it. Takes ownership of `socket`. Returns NULL if bind() or
|
||||
// connect() fail (`socket` is destroyed in that case).
|
||||
AsyncStunTCPSocket* AsyncStunTCPSocket::Create(
|
||||
rtc::Socket* socket,
|
||||
const rtc::SocketAddress& bind_address,
|
||||
const rtc::SocketAddress& remote_address) {
|
||||
return new AsyncStunTCPSocket(
|
||||
AsyncTCPSocketBase::ConnectSocket(socket, bind_address, remote_address));
|
||||
}
|
||||
|
||||
AsyncStunTCPSocket::AsyncStunTCPSocket(rtc::Socket* socket)
|
||||
: rtc::AsyncTCPSocketBase(socket, kBufSize) {}
|
||||
|
||||
int AsyncStunTCPSocket::Send(const void* pv,
|
||||
size_t cb,
|
||||
const rtc::PacketOptions& options) {
|
||||
if (cb > kBufSize || cb < kPacketLenSize + kPacketLenOffset) {
|
||||
SetError(EMSGSIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If we are blocking on send, then silently drop this packet
|
||||
if (!IsOutBufferEmpty())
|
||||
return static_cast<int>(cb);
|
||||
|
||||
int pad_bytes;
|
||||
size_t expected_pkt_len = GetExpectedLength(pv, cb, &pad_bytes);
|
||||
|
||||
// Accepts only complete STUN/ChannelData packets.
|
||||
if (cb != expected_pkt_len)
|
||||
return -1;
|
||||
|
||||
AppendToOutBuffer(pv, cb);
|
||||
|
||||
RTC_DCHECK(pad_bytes < 4);
|
||||
char padding[4] = {0};
|
||||
AppendToOutBuffer(padding, pad_bytes);
|
||||
|
||||
int res = FlushOutBuffer();
|
||||
if (res <= 0) {
|
||||
// drop packet if we made no progress
|
||||
ClearOutBuffer();
|
||||
return res;
|
||||
}
|
||||
|
||||
rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis());
|
||||
SignalSentPacket(this, sent_packet);
|
||||
|
||||
// We claim to have sent the whole thing, even if we only sent partial
|
||||
return static_cast<int>(cb);
|
||||
}
|
||||
|
||||
size_t AsyncStunTCPSocket::ProcessInput(rtc::ArrayView<const uint8_t> data) {
|
||||
rtc::SocketAddress remote_addr(GetRemoteAddress());
|
||||
// STUN packet - First 4 bytes. Total header size is 20 bytes.
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |0 0| STUN Message Type | Message Length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
// TURN ChannelData
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Channel Number | Length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
size_t processed_bytes = 0;
|
||||
while (true) {
|
||||
size_t bytes_left = data.size() - processed_bytes;
|
||||
// We need at least 4 bytes to read the STUN or ChannelData packet length.
|
||||
if (bytes_left < kPacketLenOffset + kPacketLenSize)
|
||||
return processed_bytes;
|
||||
|
||||
int pad_bytes;
|
||||
size_t expected_pkt_len = GetExpectedLength(data.data() + processed_bytes,
|
||||
bytes_left, &pad_bytes);
|
||||
size_t actual_length = expected_pkt_len + pad_bytes;
|
||||
|
||||
if (bytes_left < actual_length) {
|
||||
return processed_bytes;
|
||||
}
|
||||
|
||||
rtc::ReceivedPacket received_packet(
|
||||
data.subview(processed_bytes, expected_pkt_len), remote_addr,
|
||||
webrtc::Timestamp::Micros(rtc::TimeMicros()));
|
||||
NotifyPacketReceived(received_packet);
|
||||
processed_bytes += actual_length;
|
||||
}
|
||||
}
|
||||
|
||||
size_t AsyncStunTCPSocket::GetExpectedLength(const void* data,
|
||||
size_t len,
|
||||
int* pad_bytes) {
|
||||
*pad_bytes = 0;
|
||||
PacketLength pkt_len =
|
||||
rtc::GetBE16(static_cast<const char*>(data) + kPacketLenOffset);
|
||||
size_t expected_pkt_len;
|
||||
uint16_t msg_type = rtc::GetBE16(data);
|
||||
if (IsStunMessage(msg_type)) {
|
||||
// STUN message.
|
||||
expected_pkt_len = kStunHeaderSize + pkt_len;
|
||||
} else {
|
||||
// TURN ChannelData message.
|
||||
expected_pkt_len = kTurnChannelDataHdrSize + pkt_len;
|
||||
// From RFC 5766 section 11.5
|
||||
// Over TCP and TLS-over-TCP, the ChannelData message MUST be padded to
|
||||
// a multiple of four bytes in order to ensure the alignment of
|
||||
// subsequent messages. The padding is not reflected in the length
|
||||
// field of the ChannelData message, so the actual size of a ChannelData
|
||||
// message (including padding) is (4 + Length) rounded up to the nearest
|
||||
// multiple of 4. Over UDP, the padding is not required but MAY be
|
||||
// included.
|
||||
if (expected_pkt_len % 4)
|
||||
*pad_bytes = 4 - (expected_pkt_len % 4);
|
||||
}
|
||||
return expected_pkt_len;
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2013 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_ASYNC_STUN_TCP_SOCKET_H_
|
||||
#define P2P_BASE_ASYNC_STUN_TCP_SOCKET_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/async_tcp_socket.h"
|
||||
#include "rtc_base/socket.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class AsyncStunTCPSocket : public rtc::AsyncTCPSocketBase {
|
||||
public:
|
||||
// Binds and connects `socket` and creates AsyncTCPSocket for
|
||||
// it. Takes ownership of `socket`. Returns NULL if bind() or
|
||||
// connect() fail (`socket` is destroyed in that case).
|
||||
static AsyncStunTCPSocket* Create(rtc::Socket* socket,
|
||||
const rtc::SocketAddress& bind_address,
|
||||
const rtc::SocketAddress& remote_address);
|
||||
|
||||
explicit AsyncStunTCPSocket(rtc::Socket* socket);
|
||||
|
||||
AsyncStunTCPSocket(const AsyncStunTCPSocket&) = delete;
|
||||
AsyncStunTCPSocket& operator=(const AsyncStunTCPSocket&) = delete;
|
||||
|
||||
int Send(const void* pv,
|
||||
size_t cb,
|
||||
const rtc::PacketOptions& options) override;
|
||||
size_t ProcessInput(rtc::ArrayView<const uint8_t> data) override;
|
||||
|
||||
private:
|
||||
// This method returns the message hdr + length written in the header.
|
||||
// This method also returns the number of padding bytes needed/added to the
|
||||
// turn message. `pad_bytes` should be used only when `is_turn` is true.
|
||||
size_t GetExpectedLength(const void* data, size_t len, int* pad_bytes);
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_ASYNC_STUN_TCP_SOCKET_H_
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/basic_async_resolver_factory.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "api/async_dns_resolver.h"
|
||||
#include "rtc_base/async_dns_resolver.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface>
|
||||
BasicAsyncDnsResolverFactory::Create() {
|
||||
return std::make_unique<AsyncDnsResolver>();
|
||||
}
|
||||
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface>
|
||||
BasicAsyncDnsResolverFactory::CreateAndResolve(
|
||||
const rtc::SocketAddress& addr,
|
||||
absl::AnyInvocable<void()> callback) {
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface> resolver = Create();
|
||||
resolver->Start(addr, std::move(callback));
|
||||
return resolver;
|
||||
}
|
||||
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface>
|
||||
BasicAsyncDnsResolverFactory::CreateAndResolve(
|
||||
const rtc::SocketAddress& addr,
|
||||
int family,
|
||||
absl::AnyInvocable<void()> callback) {
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface> resolver = Create();
|
||||
resolver->Start(addr, family, std::move(callback));
|
||||
return resolver;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_BASIC_ASYNC_RESOLVER_FACTORY_H_
|
||||
#define P2P_BASE_BASIC_ASYNC_RESOLVER_FACTORY_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "api/async_dns_resolver.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// A factory that vends AsyncDnsResolver instances.
|
||||
class BasicAsyncDnsResolverFactory final
|
||||
: public AsyncDnsResolverFactoryInterface {
|
||||
public:
|
||||
BasicAsyncDnsResolverFactory() = default;
|
||||
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface> CreateAndResolve(
|
||||
const rtc::SocketAddress& addr,
|
||||
absl::AnyInvocable<void()> callback) override;
|
||||
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface> CreateAndResolve(
|
||||
const rtc::SocketAddress& addr,
|
||||
int family,
|
||||
absl::AnyInvocable<void()> callback) override;
|
||||
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface> Create() override;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // P2P_BASE_BASIC_ASYNC_RESOLVER_FACTORY_H_
|
||||
856
TMessagesProj/jni/voip/webrtc/p2p/base/basic_ice_controller.cc
Normal file
856
TMessagesProj/jni/voip/webrtc/p2p/base/basic_ice_controller.cc
Normal file
|
|
@ -0,0 +1,856 @@
|
|||
/*
|
||||
* Copyright 2019 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/basic_ice_controller.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// The minimum improvement in RTT that justifies a switch.
|
||||
const int kMinImprovement = 10;
|
||||
|
||||
bool IsRelayRelay(const cricket::Connection* conn) {
|
||||
return conn->local_candidate().is_relay() &&
|
||||
conn->remote_candidate().is_relay();
|
||||
}
|
||||
|
||||
bool IsUdp(const cricket::Connection* conn) {
|
||||
return conn->local_candidate().relay_protocol() == cricket::UDP_PROTOCOL_NAME;
|
||||
}
|
||||
|
||||
// TODO(qingsi) Use an enum to replace the following constants for all
|
||||
// comparision results.
|
||||
static constexpr int a_is_better = 1;
|
||||
static constexpr int b_is_better = -1;
|
||||
static constexpr int a_and_b_equal = 0;
|
||||
|
||||
bool LocalCandidateUsesPreferredNetwork(
|
||||
const cricket::Connection* conn,
|
||||
absl::optional<rtc::AdapterType> network_preference) {
|
||||
rtc::AdapterType network_type = conn->network()->type();
|
||||
return network_preference.has_value() && (network_type == network_preference);
|
||||
}
|
||||
|
||||
int CompareCandidatePairsByNetworkPreference(
|
||||
const cricket::Connection* a,
|
||||
const cricket::Connection* b,
|
||||
absl::optional<rtc::AdapterType> network_preference) {
|
||||
bool a_uses_preferred_network =
|
||||
LocalCandidateUsesPreferredNetwork(a, network_preference);
|
||||
bool b_uses_preferred_network =
|
||||
LocalCandidateUsesPreferredNetwork(b, network_preference);
|
||||
if (a_uses_preferred_network && !b_uses_preferred_network) {
|
||||
return a_is_better;
|
||||
} else if (!a_uses_preferred_network && b_uses_preferred_network) {
|
||||
return b_is_better;
|
||||
}
|
||||
return a_and_b_equal;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace cricket {
|
||||
|
||||
BasicIceController::BasicIceController(const IceControllerFactoryArgs& args)
|
||||
: ice_transport_state_func_(args.ice_transport_state_func),
|
||||
ice_role_func_(args.ice_role_func),
|
||||
is_connection_pruned_func_(args.is_connection_pruned_func),
|
||||
field_trials_(args.ice_field_trials) {}
|
||||
|
||||
BasicIceController::~BasicIceController() {}
|
||||
|
||||
void BasicIceController::SetIceConfig(const IceConfig& config) {
|
||||
config_ = config;
|
||||
}
|
||||
|
||||
void BasicIceController::SetSelectedConnection(
|
||||
const Connection* selected_connection) {
|
||||
selected_connection_ = selected_connection;
|
||||
}
|
||||
|
||||
void BasicIceController::AddConnection(const Connection* connection) {
|
||||
connections_.push_back(connection);
|
||||
unpinged_connections_.insert(connection);
|
||||
}
|
||||
|
||||
void BasicIceController::OnConnectionDestroyed(const Connection* connection) {
|
||||
pinged_connections_.erase(connection);
|
||||
unpinged_connections_.erase(connection);
|
||||
connections_.erase(absl::c_find(connections_, connection));
|
||||
if (selected_connection_ == connection)
|
||||
selected_connection_ = nullptr;
|
||||
}
|
||||
|
||||
bool BasicIceController::HasPingableConnection() const {
|
||||
int64_t now = rtc::TimeMillis();
|
||||
return absl::c_any_of(connections_, [this, now](const Connection* c) {
|
||||
return IsPingable(c, now);
|
||||
});
|
||||
}
|
||||
|
||||
IceControllerInterface::PingResult BasicIceController::SelectConnectionToPing(
|
||||
int64_t last_ping_sent_ms) {
|
||||
// When the selected connection is not receiving or not writable, or any
|
||||
// active connection has not been pinged enough times, use the weak ping
|
||||
// interval.
|
||||
bool need_more_pings_at_weak_interval =
|
||||
absl::c_any_of(connections_, [](const Connection* conn) {
|
||||
return conn->active() &&
|
||||
conn->num_pings_sent() < MIN_PINGS_AT_WEAK_PING_INTERVAL;
|
||||
});
|
||||
int ping_interval = (weak() || need_more_pings_at_weak_interval)
|
||||
? weak_ping_interval()
|
||||
: strong_ping_interval();
|
||||
|
||||
const Connection* conn = nullptr;
|
||||
if (rtc::TimeMillis() >= last_ping_sent_ms + ping_interval) {
|
||||
conn = FindNextPingableConnection();
|
||||
}
|
||||
PingResult res(conn, std::min(ping_interval, check_receiving_interval()));
|
||||
return res;
|
||||
}
|
||||
|
||||
void BasicIceController::MarkConnectionPinged(const Connection* conn) {
|
||||
if (conn && pinged_connections_.insert(conn).second) {
|
||||
unpinged_connections_.erase(conn);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the next pingable connection to ping.
|
||||
const Connection* BasicIceController::FindNextPingableConnection() {
|
||||
int64_t now = rtc::TimeMillis();
|
||||
|
||||
// Rule 1: Selected connection takes priority over non-selected ones.
|
||||
if (selected_connection_ && selected_connection_->connected() &&
|
||||
selected_connection_->writable() &&
|
||||
WritableConnectionPastPingInterval(selected_connection_, now)) {
|
||||
return selected_connection_;
|
||||
}
|
||||
|
||||
// Rule 2: If the channel is weak, we need to find a new writable and
|
||||
// receiving connection, probably on a different network. If there are lots of
|
||||
// connections, it may take several seconds between two pings for every
|
||||
// non-selected connection. This will cause the receiving state of those
|
||||
// connections to be false, and thus they won't be selected. This is
|
||||
// problematic for network fail-over. We want to make sure at least one
|
||||
// connection per network is pinged frequently enough in order for it to be
|
||||
// selectable. So we prioritize one connection per network.
|
||||
// Rule 2.1: Among such connections, pick the one with the earliest
|
||||
// last-ping-sent time.
|
||||
if (weak()) {
|
||||
std::vector<const Connection*> pingable_selectable_connections;
|
||||
absl::c_copy_if(GetBestWritableConnectionPerNetwork(),
|
||||
std::back_inserter(pingable_selectable_connections),
|
||||
[this, now](const Connection* conn) {
|
||||
return WritableConnectionPastPingInterval(conn, now);
|
||||
});
|
||||
auto iter = absl::c_min_element(
|
||||
pingable_selectable_connections,
|
||||
[](const Connection* conn1, const Connection* conn2) {
|
||||
return conn1->last_ping_sent() < conn2->last_ping_sent();
|
||||
});
|
||||
if (iter != pingable_selectable_connections.end()) {
|
||||
return *iter;
|
||||
}
|
||||
}
|
||||
|
||||
// Rule 3: Triggered checks have priority over non-triggered connections.
|
||||
// Rule 3.1: Among triggered checks, oldest takes precedence.
|
||||
const Connection* oldest_triggered_check =
|
||||
FindOldestConnectionNeedingTriggeredCheck(now);
|
||||
if (oldest_triggered_check) {
|
||||
return oldest_triggered_check;
|
||||
}
|
||||
|
||||
// Rule 4: Unpinged connections have priority over pinged ones.
|
||||
RTC_CHECK(connections_.size() ==
|
||||
pinged_connections_.size() + unpinged_connections_.size());
|
||||
// If there are unpinged and pingable connections, only ping those.
|
||||
// Otherwise, treat everything as unpinged.
|
||||
// TODO(honghaiz): Instead of adding two separate vectors, we can add a state
|
||||
// "pinged" to filter out unpinged connections.
|
||||
if (absl::c_none_of(unpinged_connections_,
|
||||
[this, now](const Connection* conn) {
|
||||
return this->IsPingable(conn, now);
|
||||
})) {
|
||||
unpinged_connections_.insert(pinged_connections_.begin(),
|
||||
pinged_connections_.end());
|
||||
pinged_connections_.clear();
|
||||
}
|
||||
|
||||
// Among un-pinged pingable connections, "more pingable" takes precedence.
|
||||
std::vector<const Connection*> pingable_connections;
|
||||
absl::c_copy_if(
|
||||
unpinged_connections_, std::back_inserter(pingable_connections),
|
||||
[this, now](const Connection* conn) { return IsPingable(conn, now); });
|
||||
auto iter = absl::c_max_element(
|
||||
pingable_connections,
|
||||
[this](const Connection* conn1, const Connection* conn2) {
|
||||
// Some implementations of max_element
|
||||
// compare an element with itself.
|
||||
if (conn1 == conn2) {
|
||||
return false;
|
||||
}
|
||||
return MorePingable(conn1, conn2) == conn2;
|
||||
});
|
||||
if (iter != pingable_connections.end()) {
|
||||
return *iter;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Find "triggered checks". We ping first those connections that have
|
||||
// received a ping but have not sent a ping since receiving it
|
||||
// (last_ping_received > last_ping_sent). But we shouldn't do
|
||||
// triggered checks if the connection is already writable.
|
||||
const Connection* BasicIceController::FindOldestConnectionNeedingTriggeredCheck(
|
||||
int64_t now) {
|
||||
const Connection* oldest_needing_triggered_check = nullptr;
|
||||
for (auto* conn : connections_) {
|
||||
if (!IsPingable(conn, now)) {
|
||||
continue;
|
||||
}
|
||||
bool needs_triggered_check =
|
||||
(!conn->writable() &&
|
||||
conn->last_ping_received() > conn->last_ping_sent());
|
||||
if (needs_triggered_check &&
|
||||
(!oldest_needing_triggered_check ||
|
||||
(conn->last_ping_received() <
|
||||
oldest_needing_triggered_check->last_ping_received()))) {
|
||||
oldest_needing_triggered_check = conn;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldest_needing_triggered_check) {
|
||||
RTC_LOG(LS_INFO) << "Selecting connection for triggered check: "
|
||||
<< oldest_needing_triggered_check->ToString();
|
||||
}
|
||||
return oldest_needing_triggered_check;
|
||||
}
|
||||
|
||||
bool BasicIceController::WritableConnectionPastPingInterval(
|
||||
const Connection* conn,
|
||||
int64_t now) const {
|
||||
int interval = CalculateActiveWritablePingInterval(conn, now);
|
||||
return conn->last_ping_sent() + interval <= now;
|
||||
}
|
||||
|
||||
int BasicIceController::CalculateActiveWritablePingInterval(
|
||||
const Connection* conn,
|
||||
int64_t now) const {
|
||||
// Ping each connection at a higher rate at least
|
||||
// MIN_PINGS_AT_WEAK_PING_INTERVAL times.
|
||||
if (conn->num_pings_sent() < MIN_PINGS_AT_WEAK_PING_INTERVAL) {
|
||||
return weak_ping_interval();
|
||||
}
|
||||
|
||||
int stable_interval =
|
||||
config_.stable_writable_connection_ping_interval_or_default();
|
||||
int weak_or_stablizing_interval = std::min(
|
||||
stable_interval, WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL);
|
||||
// If the channel is weak or the connection is not stable yet, use the
|
||||
// weak_or_stablizing_interval.
|
||||
return (!weak() && conn->stable(now)) ? stable_interval
|
||||
: weak_or_stablizing_interval;
|
||||
}
|
||||
|
||||
// Is the connection in a state for us to even consider pinging the other side?
|
||||
// We consider a connection pingable even if it's not connected because that's
|
||||
// how a TCP connection is kicked into reconnecting on the active side.
|
||||
bool BasicIceController::IsPingable(const Connection* conn, int64_t now) const {
|
||||
const Candidate& remote = conn->remote_candidate();
|
||||
// We should never get this far with an empty remote ufrag.
|
||||
RTC_DCHECK(!remote.username().empty());
|
||||
if (remote.username().empty() || remote.password().empty()) {
|
||||
// If we don't have an ICE ufrag and pwd, there's no way we can ping.
|
||||
return false;
|
||||
}
|
||||
|
||||
// A failed connection will not be pinged.
|
||||
if (conn->state() == IceCandidatePairState::FAILED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// An never connected connection cannot be written to at all, so pinging is
|
||||
// out of the question. However, if it has become WRITABLE, it is in the
|
||||
// reconnecting state so ping is needed.
|
||||
if (!conn->connected() && !conn->writable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we sent a number of pings wo/ reply, skip sending more
|
||||
// until we get one.
|
||||
if (conn->TooManyOutstandingPings(field_trials_->max_outstanding_pings)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the channel is weakly connected, ping all connections.
|
||||
if (weak()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Always ping active connections regardless whether the channel is completed
|
||||
// or not, but backup connections are pinged at a slower rate.
|
||||
if (IsBackupConnection(conn)) {
|
||||
return conn->rtt_samples() == 0 ||
|
||||
(now >= conn->last_ping_response_received() +
|
||||
config_.backup_connection_ping_interval_or_default());
|
||||
}
|
||||
// Don't ping inactive non-backup connections.
|
||||
if (!conn->active()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do ping unwritable, active connections.
|
||||
if (!conn->writable()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ping writable, active connections if it's been long enough since the last
|
||||
// ping.
|
||||
return WritableConnectionPastPingInterval(conn, now);
|
||||
}
|
||||
|
||||
// A connection is considered a backup connection if the channel state
|
||||
// is completed, the connection is not the selected connection and it is active.
|
||||
bool BasicIceController::IsBackupConnection(const Connection* conn) const {
|
||||
return ice_transport_state_func_() == IceTransportState::STATE_COMPLETED &&
|
||||
conn != selected_connection_ && conn->active();
|
||||
}
|
||||
|
||||
const Connection* BasicIceController::MorePingable(const Connection* conn1,
|
||||
const Connection* conn2) {
|
||||
RTC_DCHECK(conn1 != conn2);
|
||||
if (config_.prioritize_most_likely_candidate_pairs) {
|
||||
const Connection* most_likely_to_work_conn = MostLikelyToWork(conn1, conn2);
|
||||
if (most_likely_to_work_conn) {
|
||||
return most_likely_to_work_conn;
|
||||
}
|
||||
}
|
||||
|
||||
const Connection* least_recently_pinged_conn =
|
||||
LeastRecentlyPinged(conn1, conn2);
|
||||
if (least_recently_pinged_conn) {
|
||||
return least_recently_pinged_conn;
|
||||
}
|
||||
|
||||
// During the initial state when nothing has been pinged yet, return the first
|
||||
// one in the ordered `connections_`.
|
||||
auto connections = connections_;
|
||||
return *(std::find_if(connections.begin(), connections.end(),
|
||||
[conn1, conn2](const Connection* conn) {
|
||||
return conn == conn1 || conn == conn2;
|
||||
}));
|
||||
}
|
||||
|
||||
const Connection* BasicIceController::MostLikelyToWork(
|
||||
const Connection* conn1,
|
||||
const Connection* conn2) {
|
||||
bool rr1 = IsRelayRelay(conn1);
|
||||
bool rr2 = IsRelayRelay(conn2);
|
||||
if (rr1 && !rr2) {
|
||||
return conn1;
|
||||
} else if (rr2 && !rr1) {
|
||||
return conn2;
|
||||
} else if (rr1 && rr2) {
|
||||
bool udp1 = IsUdp(conn1);
|
||||
bool udp2 = IsUdp(conn2);
|
||||
if (udp1 && !udp2) {
|
||||
return conn1;
|
||||
} else if (udp2 && udp1) {
|
||||
return conn2;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Connection* BasicIceController::LeastRecentlyPinged(
|
||||
const Connection* conn1,
|
||||
const Connection* conn2) {
|
||||
if (conn1->last_ping_sent() < conn2->last_ping_sent()) {
|
||||
return conn1;
|
||||
}
|
||||
if (conn1->last_ping_sent() > conn2->last_ping_sent()) {
|
||||
return conn2;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::map<const rtc::Network*, const Connection*>
|
||||
BasicIceController::GetBestConnectionByNetwork() const {
|
||||
// `connections_` has been sorted, so the first one in the list on a given
|
||||
// network is the best connection on the network, except that the selected
|
||||
// connection is always the best connection on the network.
|
||||
std::map<const rtc::Network*, const Connection*> best_connection_by_network;
|
||||
if (selected_connection_) {
|
||||
best_connection_by_network[selected_connection_->network()] =
|
||||
selected_connection_;
|
||||
}
|
||||
// TODO(honghaiz): Need to update this if `connections_` are not sorted.
|
||||
for (const Connection* conn : connections_) {
|
||||
const rtc::Network* network = conn->network();
|
||||
// This only inserts when the network does not exist in the map.
|
||||
best_connection_by_network.insert(std::make_pair(network, conn));
|
||||
}
|
||||
return best_connection_by_network;
|
||||
}
|
||||
|
||||
std::vector<const Connection*>
|
||||
BasicIceController::GetBestWritableConnectionPerNetwork() const {
|
||||
std::vector<const Connection*> connections;
|
||||
for (auto kv : GetBestConnectionByNetwork()) {
|
||||
const Connection* conn = kv.second;
|
||||
if (conn->writable() && conn->connected()) {
|
||||
connections.push_back(conn);
|
||||
}
|
||||
}
|
||||
return connections;
|
||||
}
|
||||
|
||||
IceControllerInterface::SwitchResult
|
||||
BasicIceController::HandleInitialSelectDampening(
|
||||
IceSwitchReason reason,
|
||||
const Connection* new_connection) {
|
||||
if (!field_trials_->initial_select_dampening.has_value() &&
|
||||
!field_trials_->initial_select_dampening_ping_received.has_value()) {
|
||||
// experiment not enabled => select connection.
|
||||
return {new_connection, absl::nullopt};
|
||||
}
|
||||
|
||||
int64_t now = rtc::TimeMillis();
|
||||
int64_t max_delay = 0;
|
||||
if (new_connection->last_ping_received() > 0 &&
|
||||
field_trials_->initial_select_dampening_ping_received.has_value()) {
|
||||
max_delay = *field_trials_->initial_select_dampening_ping_received;
|
||||
} else if (field_trials_->initial_select_dampening.has_value()) {
|
||||
max_delay = *field_trials_->initial_select_dampening;
|
||||
}
|
||||
|
||||
int64_t start_wait =
|
||||
initial_select_timestamp_ms_ == 0 ? now : initial_select_timestamp_ms_;
|
||||
int64_t max_wait_until = start_wait + max_delay;
|
||||
|
||||
if (now >= max_wait_until) {
|
||||
RTC_LOG(LS_INFO) << "reset initial_select_timestamp_ = "
|
||||
<< initial_select_timestamp_ms_
|
||||
<< " selection delayed by: " << (now - start_wait) << "ms";
|
||||
initial_select_timestamp_ms_ = 0;
|
||||
return {new_connection, absl::nullopt};
|
||||
}
|
||||
|
||||
// We are not yet ready to select first connection...
|
||||
if (initial_select_timestamp_ms_ == 0) {
|
||||
// Set timestamp on first time...
|
||||
// but run the delayed invokation everytime to
|
||||
// avoid possibility that we miss it.
|
||||
initial_select_timestamp_ms_ = now;
|
||||
RTC_LOG(LS_INFO) << "set initial_select_timestamp_ms_ = "
|
||||
<< initial_select_timestamp_ms_;
|
||||
}
|
||||
|
||||
int min_delay = max_delay;
|
||||
if (field_trials_->initial_select_dampening.has_value()) {
|
||||
min_delay = std::min(min_delay, *field_trials_->initial_select_dampening);
|
||||
}
|
||||
if (field_trials_->initial_select_dampening_ping_received.has_value()) {
|
||||
min_delay = std::min(
|
||||
min_delay, *field_trials_->initial_select_dampening_ping_received);
|
||||
}
|
||||
|
||||
RTC_LOG(LS_INFO) << "delay initial selection up to " << min_delay << "ms";
|
||||
return {.connection = absl::nullopt,
|
||||
.recheck_event = IceRecheckEvent(
|
||||
IceSwitchReason::ICE_CONTROLLER_RECHECK, min_delay)};
|
||||
}
|
||||
|
||||
IceControllerInterface::SwitchResult BasicIceController::ShouldSwitchConnection(
|
||||
IceSwitchReason reason,
|
||||
const Connection* new_connection) {
|
||||
if (!ReadyToSend(new_connection) || selected_connection_ == new_connection) {
|
||||
return {absl::nullopt, absl::nullopt};
|
||||
}
|
||||
|
||||
if (selected_connection_ == nullptr) {
|
||||
return HandleInitialSelectDampening(reason, new_connection);
|
||||
}
|
||||
|
||||
// Do not switch to a connection that is not receiving if it is not on a
|
||||
// preferred network or it has higher cost because it may be just spuriously
|
||||
// better.
|
||||
int compare_a_b_by_networks = CompareCandidatePairNetworks(
|
||||
new_connection, selected_connection_, config_.network_preference);
|
||||
if (compare_a_b_by_networks == b_is_better && !new_connection->receiving()) {
|
||||
return {absl::nullopt, absl::nullopt};
|
||||
}
|
||||
|
||||
bool missed_receiving_unchanged_threshold = false;
|
||||
absl::optional<int64_t> receiving_unchanged_threshold(
|
||||
rtc::TimeMillis() - config_.receiving_switching_delay_or_default());
|
||||
int cmp = CompareConnections(selected_connection_, new_connection,
|
||||
receiving_unchanged_threshold,
|
||||
&missed_receiving_unchanged_threshold);
|
||||
|
||||
absl::optional<IceRecheckEvent> recheck_event;
|
||||
if (missed_receiving_unchanged_threshold &&
|
||||
config_.receiving_switching_delay_or_default()) {
|
||||
// If we do not switch to the connection because it missed the receiving
|
||||
// threshold, the new connection is in a better receiving state than the
|
||||
// currently selected connection. So we need to re-check whether it needs
|
||||
// to be switched at a later time.
|
||||
recheck_event.emplace(reason,
|
||||
config_.receiving_switching_delay_or_default());
|
||||
}
|
||||
|
||||
if (cmp < 0) {
|
||||
return {new_connection, absl::nullopt};
|
||||
} else if (cmp > 0) {
|
||||
return {absl::nullopt, recheck_event};
|
||||
}
|
||||
|
||||
// If everything else is the same, switch only if rtt has improved by
|
||||
// a margin.
|
||||
if (new_connection->rtt() <= selected_connection_->rtt() - kMinImprovement) {
|
||||
return {new_connection, absl::nullopt};
|
||||
}
|
||||
|
||||
return {absl::nullopt, recheck_event};
|
||||
}
|
||||
|
||||
IceControllerInterface::SwitchResult
|
||||
BasicIceController::SortAndSwitchConnection(IceSwitchReason reason) {
|
||||
// Find the best alternative connection by sorting. It is important to note
|
||||
// that amongst equal preference, writable connections, this will choose the
|
||||
// one whose estimated latency is lowest. So it is the only one that we
|
||||
// need to consider switching to.
|
||||
// TODO(honghaiz): Don't sort; Just use std::max_element in the right places.
|
||||
absl::c_stable_sort(
|
||||
connections_, [this](const Connection* a, const Connection* b) {
|
||||
int cmp = CompareConnections(a, b, absl::nullopt, nullptr);
|
||||
if (cmp != 0) {
|
||||
return cmp > 0;
|
||||
}
|
||||
// Otherwise, sort based on latency estimate.
|
||||
return a->rtt() < b->rtt();
|
||||
});
|
||||
|
||||
RTC_LOG(LS_VERBOSE) << "Sorting " << connections_.size()
|
||||
<< " available connections due to: "
|
||||
<< IceSwitchReasonToString(reason);
|
||||
for (size_t i = 0; i < connections_.size(); ++i) {
|
||||
RTC_LOG(LS_VERBOSE) << connections_[i]->ToString();
|
||||
}
|
||||
|
||||
const Connection* top_connection =
|
||||
(!connections_.empty()) ? connections_[0] : nullptr;
|
||||
|
||||
return ShouldSwitchConnection(reason, top_connection);
|
||||
}
|
||||
|
||||
bool BasicIceController::ReadyToSend(const Connection* connection) const {
|
||||
// Note that we allow sending on an unreliable connection, because it's
|
||||
// possible that it became unreliable simply due to bad chance.
|
||||
// So this shouldn't prevent attempting to send media.
|
||||
return connection != nullptr &&
|
||||
(connection->writable() ||
|
||||
connection->write_state() == Connection::STATE_WRITE_UNRELIABLE ||
|
||||
PresumedWritable(connection));
|
||||
}
|
||||
|
||||
bool BasicIceController::PresumedWritable(const Connection* conn) const {
|
||||
return (conn->write_state() == Connection::STATE_WRITE_INIT &&
|
||||
config_.presume_writable_when_fully_relayed &&
|
||||
conn->local_candidate().is_relay() &&
|
||||
(conn->remote_candidate().is_relay() ||
|
||||
conn->remote_candidate().is_prflx()));
|
||||
}
|
||||
|
||||
// Compare two connections based on their writing, receiving, and connected
|
||||
// states.
|
||||
int BasicIceController::CompareConnectionStates(
|
||||
const Connection* a,
|
||||
const Connection* b,
|
||||
absl::optional<int64_t> receiving_unchanged_threshold,
|
||||
bool* missed_receiving_unchanged_threshold) const {
|
||||
// First, prefer a connection that's writable or presumed writable over
|
||||
// one that's not writable.
|
||||
bool a_writable = a->writable() || PresumedWritable(a);
|
||||
bool b_writable = b->writable() || PresumedWritable(b);
|
||||
if (a_writable && !b_writable) {
|
||||
return a_is_better;
|
||||
}
|
||||
if (!a_writable && b_writable) {
|
||||
return b_is_better;
|
||||
}
|
||||
|
||||
// Sort based on write-state. Better states have lower values.
|
||||
if (a->write_state() < b->write_state()) {
|
||||
return a_is_better;
|
||||
}
|
||||
if (b->write_state() < a->write_state()) {
|
||||
return b_is_better;
|
||||
}
|
||||
|
||||
// We prefer a receiving connection to a non-receiving, higher-priority
|
||||
// connection when sorting connections and choosing which connection to
|
||||
// switch to.
|
||||
if (a->receiving() && !b->receiving()) {
|
||||
return a_is_better;
|
||||
}
|
||||
if (!a->receiving() && b->receiving()) {
|
||||
if (!receiving_unchanged_threshold ||
|
||||
(a->receiving_unchanged_since() <= *receiving_unchanged_threshold &&
|
||||
b->receiving_unchanged_since() <= *receiving_unchanged_threshold)) {
|
||||
return b_is_better;
|
||||
}
|
||||
*missed_receiving_unchanged_threshold = true;
|
||||
}
|
||||
|
||||
// WARNING: Some complexity here about TCP reconnecting.
|
||||
// When a TCP connection fails because of a TCP socket disconnecting, the
|
||||
// active side of the connection will attempt to reconnect for 5 seconds while
|
||||
// pretending to be writable (the connection is not set to the unwritable
|
||||
// state). On the passive side, the connection also remains writable even
|
||||
// though it is disconnected, and a new connection is created when the active
|
||||
// side connects. At that point, there are two TCP connections on the passive
|
||||
// side: 1. the old, disconnected one that is pretending to be writable, and
|
||||
// 2. the new, connected one that is maybe not yet writable. For purposes of
|
||||
// pruning, pinging, and selecting the selected connection, we want to treat
|
||||
// the new connection as "better" than the old one. We could add a method
|
||||
// called something like Connection::ImReallyBadEvenThoughImWritable, but that
|
||||
// is equivalent to the existing Connection::connected(), which we already
|
||||
// have. So, in code throughout this file, we'll check whether the connection
|
||||
// is connected() or not, and if it is not, treat it as "worse" than a
|
||||
// connected one, even though it's writable. In the code below, we're doing
|
||||
// so to make sure we treat a new writable connection as better than an old
|
||||
// disconnected connection.
|
||||
|
||||
// In the case where we reconnect TCP connections, the original best
|
||||
// connection is disconnected without changing to WRITE_TIMEOUT. In this case,
|
||||
// the new connection, when it becomes writable, should have higher priority.
|
||||
if (a->write_state() == Connection::STATE_WRITABLE &&
|
||||
b->write_state() == Connection::STATE_WRITABLE) {
|
||||
if (a->connected() && !b->connected()) {
|
||||
return a_is_better;
|
||||
}
|
||||
if (!a->connected() && b->connected()) {
|
||||
return b_is_better;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Compares two connections based only on the candidate and network information.
|
||||
// Returns positive if `a` is better than `b`.
|
||||
int BasicIceController::CompareConnectionCandidates(const Connection* a,
|
||||
const Connection* b) const {
|
||||
int compare_a_b_by_networks =
|
||||
CompareCandidatePairNetworks(a, b, config_.network_preference);
|
||||
if (compare_a_b_by_networks != a_and_b_equal) {
|
||||
return compare_a_b_by_networks;
|
||||
}
|
||||
|
||||
// Compare connection priority. Lower values get sorted last.
|
||||
if (a->priority() > b->priority()) {
|
||||
return a_is_better;
|
||||
}
|
||||
if (a->priority() < b->priority()) {
|
||||
return b_is_better;
|
||||
}
|
||||
|
||||
// If we're still tied at this point, prefer a younger generation.
|
||||
// (Younger generation means a larger generation number).
|
||||
int cmp = (a->remote_candidate().generation() + a->generation()) -
|
||||
(b->remote_candidate().generation() + b->generation());
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
|
||||
// A periodic regather (triggered by the regather_all_networks_interval_range)
|
||||
// will produce candidates that appear the same but would use a new port. We
|
||||
// want to use the new candidates and purge the old candidates as they come
|
||||
// in, so use the fact that the old ports get pruned immediately to rank the
|
||||
// candidates with an active port/remote candidate higher.
|
||||
bool a_pruned = is_connection_pruned_func_(a);
|
||||
bool b_pruned = is_connection_pruned_func_(b);
|
||||
if (!a_pruned && b_pruned) {
|
||||
return a_is_better;
|
||||
}
|
||||
if (a_pruned && !b_pruned) {
|
||||
return b_is_better;
|
||||
}
|
||||
|
||||
// Otherwise, must be equal
|
||||
return 0;
|
||||
}
|
||||
|
||||
int BasicIceController::CompareConnections(
|
||||
const Connection* a,
|
||||
const Connection* b,
|
||||
absl::optional<int64_t> receiving_unchanged_threshold,
|
||||
bool* missed_receiving_unchanged_threshold) const {
|
||||
RTC_CHECK(a != nullptr);
|
||||
RTC_CHECK(b != nullptr);
|
||||
|
||||
// We prefer to switch to a writable and receiving connection over a
|
||||
// non-writable or non-receiving connection, even if the latter has
|
||||
// been nominated by the controlling side.
|
||||
int state_cmp = CompareConnectionStates(a, b, receiving_unchanged_threshold,
|
||||
missed_receiving_unchanged_threshold);
|
||||
if (state_cmp != 0) {
|
||||
return state_cmp;
|
||||
}
|
||||
|
||||
if (ice_role_func_() == ICEROLE_CONTROLLED) {
|
||||
// Compare the connections based on the nomination states and the last data
|
||||
// received time if this is on the controlled side.
|
||||
if (a->remote_nomination() > b->remote_nomination()) {
|
||||
return a_is_better;
|
||||
}
|
||||
if (a->remote_nomination() < b->remote_nomination()) {
|
||||
return b_is_better;
|
||||
}
|
||||
|
||||
if (a->last_data_received() > b->last_data_received()) {
|
||||
return a_is_better;
|
||||
}
|
||||
if (a->last_data_received() < b->last_data_received()) {
|
||||
return b_is_better;
|
||||
}
|
||||
}
|
||||
|
||||
// Compare the network cost and priority.
|
||||
return CompareConnectionCandidates(a, b);
|
||||
}
|
||||
|
||||
int BasicIceController::CompareCandidatePairNetworks(
|
||||
const Connection* a,
|
||||
const Connection* b,
|
||||
absl::optional<rtc::AdapterType> network_preference) const {
|
||||
int compare_a_b_by_network_preference =
|
||||
CompareCandidatePairsByNetworkPreference(a, b,
|
||||
config_.network_preference);
|
||||
// The network preference has a higher precedence than the network cost.
|
||||
if (compare_a_b_by_network_preference != a_and_b_equal) {
|
||||
return compare_a_b_by_network_preference;
|
||||
}
|
||||
|
||||
bool a_vpn = a->network()->IsVpn();
|
||||
bool b_vpn = b->network()->IsVpn();
|
||||
switch (config_.vpn_preference) {
|
||||
case webrtc::VpnPreference::kDefault:
|
||||
break;
|
||||
case webrtc::VpnPreference::kOnlyUseVpn:
|
||||
case webrtc::VpnPreference::kPreferVpn:
|
||||
if (a_vpn && !b_vpn) {
|
||||
return a_is_better;
|
||||
} else if (!a_vpn && b_vpn) {
|
||||
return b_is_better;
|
||||
}
|
||||
break;
|
||||
case webrtc::VpnPreference::kNeverUseVpn:
|
||||
case webrtc::VpnPreference::kAvoidVpn:
|
||||
if (a_vpn && !b_vpn) {
|
||||
return b_is_better;
|
||||
} else if (!a_vpn && b_vpn) {
|
||||
return a_is_better;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t a_cost = a->ComputeNetworkCost();
|
||||
uint32_t b_cost = b->ComputeNetworkCost();
|
||||
// Prefer lower network cost.
|
||||
if (a_cost < b_cost) {
|
||||
return a_is_better;
|
||||
}
|
||||
if (a_cost > b_cost) {
|
||||
return b_is_better;
|
||||
}
|
||||
return a_and_b_equal;
|
||||
}
|
||||
|
||||
std::vector<const Connection*> BasicIceController::PruneConnections() {
|
||||
// We can prune any connection for which there is a connected, writable
|
||||
// connection on the same network with better or equal priority. We leave
|
||||
// those with better priority just in case they become writable later (at
|
||||
// which point, we would prune out the current selected connection). We leave
|
||||
// connections on other networks because they may not be using the same
|
||||
// resources and they may represent very distinct paths over which we can
|
||||
// switch. If `best_conn_on_network` is not connected, we may be reconnecting
|
||||
// a TCP connection and should not prune connections in this network.
|
||||
// See the big comment in CompareConnectionStates.
|
||||
//
|
||||
// An exception is made for connections on an "any address" network, meaning
|
||||
// not bound to any specific network interface. We don't want to keep one of
|
||||
// these alive as a backup, since it could be using the same network
|
||||
// interface as the higher-priority, selected candidate pair.
|
||||
std::vector<const Connection*> connections_to_prune;
|
||||
auto best_connection_by_network = GetBestConnectionByNetwork();
|
||||
for (const Connection* conn : connections_) {
|
||||
const Connection* best_conn = selected_connection_;
|
||||
if (!rtc::IPIsAny(conn->network()->GetBestIP())) {
|
||||
// If the connection is bound to a specific network interface (not an
|
||||
// "any address" network), compare it against the best connection for
|
||||
// that network interface rather than the best connection overall. This
|
||||
// ensures that at least one connection per network will be left
|
||||
// unpruned.
|
||||
best_conn = best_connection_by_network[conn->network()];
|
||||
}
|
||||
// Do not prune connections if the connection being compared against is
|
||||
// weak. Otherwise, it may delete connections prematurely.
|
||||
if (best_conn && conn != best_conn && !best_conn->weak() &&
|
||||
CompareConnectionCandidates(best_conn, conn) >= 0) {
|
||||
connections_to_prune.push_back(conn);
|
||||
}
|
||||
}
|
||||
return connections_to_prune;
|
||||
}
|
||||
|
||||
bool BasicIceController::GetUseCandidateAttr(const Connection* conn,
|
||||
NominationMode mode,
|
||||
IceMode remote_ice_mode) const {
|
||||
switch (mode) {
|
||||
case NominationMode::REGULAR:
|
||||
// TODO(honghaiz): Implement regular nomination.
|
||||
return false;
|
||||
case NominationMode::AGGRESSIVE:
|
||||
if (remote_ice_mode == ICEMODE_LITE) {
|
||||
return GetUseCandidateAttr(conn, NominationMode::REGULAR,
|
||||
remote_ice_mode);
|
||||
}
|
||||
return true;
|
||||
case NominationMode::SEMI_AGGRESSIVE: {
|
||||
// Nominate if
|
||||
// a) Remote is in FULL ICE AND
|
||||
// a.1) `conn` is the selected connection OR
|
||||
// a.2) there is no selected connection OR
|
||||
// a.3) the selected connection is unwritable OR
|
||||
// a.4) `conn` has higher priority than selected_connection.
|
||||
// b) Remote is in LITE ICE AND
|
||||
// b.1) `conn` is the selected_connection AND
|
||||
// b.2) `conn` is writable.
|
||||
bool selected = conn == selected_connection_;
|
||||
if (remote_ice_mode == ICEMODE_LITE) {
|
||||
return selected && conn->writable();
|
||||
}
|
||||
bool better_than_selected =
|
||||
!selected_connection_ || !selected_connection_->writable() ||
|
||||
CompareConnectionCandidates(selected_connection_, conn) < 0;
|
||||
return selected || better_than_selected;
|
||||
}
|
||||
default:
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
167
TMessagesProj/jni/voip/webrtc/p2p/base/basic_ice_controller.h
Normal file
167
TMessagesProj/jni/voip/webrtc/p2p/base/basic_ice_controller.h
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Copyright 2019 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_BASIC_ICE_CONTROLLER_H_
|
||||
#define P2P_BASE_BASIC_ICE_CONTROLLER_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "p2p/base/ice_controller_factory_interface.h"
|
||||
#include "p2p/base/ice_controller_interface.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class BasicIceController : public IceControllerInterface {
|
||||
public:
|
||||
explicit BasicIceController(const IceControllerFactoryArgs& args);
|
||||
virtual ~BasicIceController();
|
||||
|
||||
void SetIceConfig(const IceConfig& config) override;
|
||||
void SetSelectedConnection(const Connection* selected_connection) override;
|
||||
void AddConnection(const Connection* connection) override;
|
||||
void OnConnectionDestroyed(const Connection* connection) override;
|
||||
rtc::ArrayView<const Connection* const> GetConnections() const override {
|
||||
return connections_;
|
||||
}
|
||||
rtc::ArrayView<const Connection*> connections() const override {
|
||||
return rtc::ArrayView<const Connection*>(
|
||||
const_cast<const Connection**>(connections_.data()),
|
||||
connections_.size());
|
||||
}
|
||||
|
||||
bool HasPingableConnection() const override;
|
||||
|
||||
PingResult SelectConnectionToPing(int64_t last_ping_sent_ms) override;
|
||||
|
||||
bool GetUseCandidateAttr(const Connection* conn,
|
||||
NominationMode mode,
|
||||
IceMode remote_ice_mode) const override;
|
||||
|
||||
SwitchResult ShouldSwitchConnection(IceSwitchReason reason,
|
||||
const Connection* connection) override;
|
||||
SwitchResult SortAndSwitchConnection(IceSwitchReason reason) override;
|
||||
|
||||
std::vector<const Connection*> PruneConnections() override;
|
||||
|
||||
// These methods are only for tests.
|
||||
const Connection* FindNextPingableConnection() override;
|
||||
void MarkConnectionPinged(const Connection* conn) override;
|
||||
|
||||
private:
|
||||
// A transport channel is weak if the current best connection is either
|
||||
// not receiving or not writable, or if there is no best connection at all.
|
||||
bool weak() const {
|
||||
return !selected_connection_ || selected_connection_->weak();
|
||||
}
|
||||
|
||||
int weak_ping_interval() const {
|
||||
return std::max(config_.ice_check_interval_weak_connectivity_or_default(),
|
||||
config_.ice_check_min_interval_or_default());
|
||||
}
|
||||
|
||||
int strong_ping_interval() const {
|
||||
return std::max(config_.ice_check_interval_strong_connectivity_or_default(),
|
||||
config_.ice_check_min_interval_or_default());
|
||||
}
|
||||
|
||||
int check_receiving_interval() const {
|
||||
return std::max(MIN_CHECK_RECEIVING_INTERVAL,
|
||||
config_.receiving_timeout_or_default() / 10);
|
||||
}
|
||||
|
||||
const Connection* FindOldestConnectionNeedingTriggeredCheck(int64_t now);
|
||||
// Between `conn1` and `conn2`, this function returns the one which should
|
||||
// be pinged first.
|
||||
const Connection* MorePingable(const Connection* conn1,
|
||||
const Connection* conn2);
|
||||
// Select the connection which is Relay/Relay. If both of them are,
|
||||
// UDP relay protocol takes precedence.
|
||||
const Connection* MostLikelyToWork(const Connection* conn1,
|
||||
const Connection* conn2);
|
||||
// Compare the last_ping_sent time and return the one least recently pinged.
|
||||
const Connection* LeastRecentlyPinged(const Connection* conn1,
|
||||
const Connection* conn2);
|
||||
|
||||
bool IsPingable(const Connection* conn, int64_t now) const;
|
||||
bool IsBackupConnection(const Connection* conn) const;
|
||||
// Whether a writable connection is past its ping interval and needs to be
|
||||
// pinged again.
|
||||
bool WritableConnectionPastPingInterval(const Connection* conn,
|
||||
int64_t now) const;
|
||||
int CalculateActiveWritablePingInterval(const Connection* conn,
|
||||
int64_t now) const;
|
||||
|
||||
std::map<const rtc::Network*, const Connection*> GetBestConnectionByNetwork()
|
||||
const;
|
||||
std::vector<const Connection*> GetBestWritableConnectionPerNetwork() const;
|
||||
|
||||
bool ReadyToSend(const Connection* connection) const;
|
||||
bool PresumedWritable(const Connection* conn) const;
|
||||
|
||||
int CompareCandidatePairNetworks(
|
||||
const Connection* a,
|
||||
const Connection* b,
|
||||
absl::optional<rtc::AdapterType> network_preference) const;
|
||||
|
||||
// The methods below return a positive value if `a` is preferable to `b`,
|
||||
// a negative value if `b` is preferable, and 0 if they're equally preferable.
|
||||
// If `receiving_unchanged_threshold` is set, then when `b` is receiving and
|
||||
// `a` is not, returns a negative value only if `b` has been in receiving
|
||||
// state and `a` has been in not receiving state since
|
||||
// `receiving_unchanged_threshold` and sets
|
||||
// `missed_receiving_unchanged_threshold` to true otherwise.
|
||||
int CompareConnectionStates(
|
||||
const Connection* a,
|
||||
const Connection* b,
|
||||
absl::optional<int64_t> receiving_unchanged_threshold,
|
||||
bool* missed_receiving_unchanged_threshold) const;
|
||||
int CompareConnectionCandidates(const Connection* a,
|
||||
const Connection* b) const;
|
||||
// Compares two connections based on the connection states
|
||||
// (writable/receiving/connected), nomination states, last data received time,
|
||||
// and static preferences. Does not include latency. Used by both sorting
|
||||
// and ShouldSwitchSelectedConnection().
|
||||
// Returns a positive value if `a` is better than `b`.
|
||||
int CompareConnections(const Connection* a,
|
||||
const Connection* b,
|
||||
absl::optional<int64_t> receiving_unchanged_threshold,
|
||||
bool* missed_receiving_unchanged_threshold) const;
|
||||
|
||||
SwitchResult HandleInitialSelectDampening(IceSwitchReason reason,
|
||||
const Connection* new_connection);
|
||||
|
||||
std::function<IceTransportState()> ice_transport_state_func_;
|
||||
std::function<IceRole()> ice_role_func_;
|
||||
std::function<bool(const Connection*)> is_connection_pruned_func_;
|
||||
|
||||
IceConfig config_;
|
||||
const IceFieldTrials* field_trials_;
|
||||
|
||||
// `connections_` is a sorted list with the first one always be the
|
||||
// `selected_connection_` when it's not nullptr. The combination of
|
||||
// `pinged_connections_` and `unpinged_connections_` has the same
|
||||
// connections as `connections_`. These 2 sets maintain whether a
|
||||
// connection should be pinged next or not.
|
||||
const Connection* selected_connection_ = nullptr;
|
||||
std::vector<const Connection*> connections_;
|
||||
std::set<const Connection*> pinged_connections_;
|
||||
std::set<const Connection*> unpinged_connections_;
|
||||
|
||||
// Timestamp for when we got the first selectable connection.
|
||||
int64_t initial_select_timestamp_ms_ = 0;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_BASIC_ICE_CONTROLLER_H_
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Copyright 2011 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/basic_packet_socket_factory.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "api/async_dns_resolver.h"
|
||||
#include "p2p/base/async_stun_tcp_socket.h"
|
||||
#include "rtc_base/async_dns_resolver.h"
|
||||
#include "rtc_base/async_tcp_socket.h"
|
||||
#include "rtc_base/async_udp_socket.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/socket.h"
|
||||
#include "rtc_base/socket_adapters.h"
|
||||
#include "rtc_base/ssl_adapter.h"
|
||||
|
||||
namespace rtc {
|
||||
|
||||
BasicPacketSocketFactory::BasicPacketSocketFactory(
|
||||
SocketFactory* socket_factory)
|
||||
: socket_factory_(socket_factory) {}
|
||||
|
||||
BasicPacketSocketFactory::~BasicPacketSocketFactory() {}
|
||||
|
||||
AsyncPacketSocket* BasicPacketSocketFactory::CreateUdpSocket(
|
||||
const SocketAddress& address,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port) {
|
||||
// UDP sockets are simple.
|
||||
Socket* socket = socket_factory_->CreateSocket(address.family(), SOCK_DGRAM);
|
||||
if (!socket) {
|
||||
return NULL;
|
||||
}
|
||||
if (BindSocket(socket, address, min_port, max_port) < 0) {
|
||||
RTC_LOG(LS_ERROR) << "UDP bind failed with error " << socket->GetError();
|
||||
delete socket;
|
||||
return NULL;
|
||||
}
|
||||
return new AsyncUDPSocket(socket);
|
||||
}
|
||||
|
||||
AsyncListenSocket* BasicPacketSocketFactory::CreateServerTcpSocket(
|
||||
const SocketAddress& local_address,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
int opts) {
|
||||
// Fail if TLS is required.
|
||||
if (opts & PacketSocketFactory::OPT_TLS) {
|
||||
RTC_LOG(LS_ERROR) << "TLS support currently is not available.";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (opts & PacketSocketFactory::OPT_TLS_FAKE) {
|
||||
RTC_LOG(LS_ERROR) << "Fake TLS not supported.";
|
||||
return NULL;
|
||||
}
|
||||
Socket* socket =
|
||||
socket_factory_->CreateSocket(local_address.family(), SOCK_STREAM);
|
||||
if (!socket) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (BindSocket(socket, local_address, min_port, max_port) < 0) {
|
||||
RTC_LOG(LS_ERROR) << "TCP bind failed with error " << socket->GetError();
|
||||
delete socket;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RTC_CHECK(!(opts & PacketSocketFactory::OPT_STUN));
|
||||
|
||||
return new AsyncTcpListenSocket(absl::WrapUnique(socket));
|
||||
}
|
||||
|
||||
AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket(
|
||||
const SocketAddress& local_address,
|
||||
const SocketAddress& remote_address,
|
||||
const ProxyInfo& proxy_info,
|
||||
const std::string& user_agent,
|
||||
const PacketSocketTcpOptions& tcp_options) {
|
||||
Socket* socket =
|
||||
socket_factory_->CreateSocket(local_address.family(), SOCK_STREAM);
|
||||
if (!socket) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (BindSocket(socket, local_address, 0, 0) < 0) {
|
||||
// Allow BindSocket to fail if we're binding to the ANY address, since this
|
||||
// is mostly redundant in the first place. The socket will be bound when we
|
||||
// call Connect() instead.
|
||||
if (local_address.IsAnyIP()) {
|
||||
RTC_LOG(LS_WARNING) << "TCP bind failed with error " << socket->GetError()
|
||||
<< "; ignoring since socket is using 'any' address.";
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "TCP bind failed with error " << socket->GetError();
|
||||
delete socket;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Set TCP_NODELAY (via OPT_NODELAY) for improved performance; this causes
|
||||
// small media packets to be sent immediately rather than being buffered up,
|
||||
// reducing latency.
|
||||
//
|
||||
// Must be done before calling Connect, otherwise it may fail.
|
||||
if (socket->SetOption(Socket::OPT_NODELAY, 1) != 0) {
|
||||
RTC_LOG(LS_ERROR) << "Setting TCP_NODELAY option failed with error "
|
||||
<< socket->GetError();
|
||||
}
|
||||
|
||||
if (proxy_info.type == PROXY_HTTPS) {
|
||||
socket =
|
||||
new AsyncHttpsProxySocket(socket, user_agent, proxy_info.address,
|
||||
proxy_info.username, proxy_info.password);
|
||||
}
|
||||
|
||||
// Assert that at most one TLS option is used.
|
||||
int tlsOpts = tcp_options.opts & (PacketSocketFactory::OPT_TLS |
|
||||
PacketSocketFactory::OPT_TLS_FAKE |
|
||||
PacketSocketFactory::OPT_TLS_INSECURE);
|
||||
RTC_DCHECK((tlsOpts & (tlsOpts - 1)) == 0);
|
||||
|
||||
if ((tlsOpts & PacketSocketFactory::OPT_TLS) ||
|
||||
(tlsOpts & PacketSocketFactory::OPT_TLS_INSECURE)) {
|
||||
// Using TLS, wrap the socket in an SSL adapter.
|
||||
SSLAdapter* ssl_adapter = SSLAdapter::Create(socket);
|
||||
if (!ssl_adapter) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (tlsOpts & PacketSocketFactory::OPT_TLS_INSECURE) {
|
||||
ssl_adapter->SetIgnoreBadCert(true);
|
||||
}
|
||||
|
||||
ssl_adapter->SetAlpnProtocols(tcp_options.tls_alpn_protocols);
|
||||
ssl_adapter->SetEllipticCurves(tcp_options.tls_elliptic_curves);
|
||||
ssl_adapter->SetCertVerifier(tcp_options.tls_cert_verifier);
|
||||
|
||||
socket = ssl_adapter;
|
||||
|
||||
if (ssl_adapter->StartSSL(remote_address.hostname().c_str()) != 0) {
|
||||
delete ssl_adapter;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} else if (tlsOpts & PacketSocketFactory::OPT_TLS_FAKE) {
|
||||
// Using fake TLS, wrap the TCP socket in a pseudo-SSL socket.
|
||||
socket = new AsyncSSLSocket(socket);
|
||||
}
|
||||
|
||||
if (socket->Connect(remote_address) < 0) {
|
||||
RTC_LOG(LS_ERROR) << "TCP connect failed with error " << socket->GetError();
|
||||
delete socket;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Finally, wrap that socket in a TCP or STUN TCP packet socket.
|
||||
AsyncPacketSocket* tcp_socket;
|
||||
if (tcp_options.opts & PacketSocketFactory::OPT_STUN) {
|
||||
tcp_socket = new cricket::AsyncStunTCPSocket(socket);
|
||||
} else {
|
||||
tcp_socket = new AsyncTCPSocket(socket);
|
||||
}
|
||||
|
||||
return tcp_socket;
|
||||
}
|
||||
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface>
|
||||
BasicPacketSocketFactory::CreateAsyncDnsResolver() {
|
||||
return std::make_unique<webrtc::AsyncDnsResolver>();
|
||||
}
|
||||
|
||||
int BasicPacketSocketFactory::BindSocket(Socket* socket,
|
||||
const SocketAddress& local_address,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port) {
|
||||
int ret = -1;
|
||||
if (min_port == 0 && max_port == 0) {
|
||||
// If there's no port range, let the OS pick a port for us.
|
||||
ret = socket->Bind(local_address);
|
||||
} else {
|
||||
// Otherwise, try to find a port in the provided range.
|
||||
for (int port = min_port; ret < 0 && port <= max_port; ++port) {
|
||||
ret = socket->Bind(SocketAddress(local_address.ipaddr(), port));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2011 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_BASIC_PACKET_SOCKET_FACTORY_H_
|
||||
#define P2P_BASE_BASIC_PACKET_SOCKET_FACTORY_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "api/async_dns_resolver.h"
|
||||
#include "api/packet_socket_factory.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/proxy_info.h"
|
||||
#include "rtc_base/socket.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/socket_factory.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace rtc {
|
||||
|
||||
class SocketFactory;
|
||||
|
||||
class RTC_EXPORT BasicPacketSocketFactory : public PacketSocketFactory {
|
||||
public:
|
||||
explicit BasicPacketSocketFactory(SocketFactory* socket_factory);
|
||||
~BasicPacketSocketFactory() override;
|
||||
|
||||
AsyncPacketSocket* CreateUdpSocket(const SocketAddress& local_address,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port) override;
|
||||
AsyncListenSocket* CreateServerTcpSocket(const SocketAddress& local_address,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
int opts) override;
|
||||
AsyncPacketSocket* CreateClientTcpSocket(
|
||||
const SocketAddress& local_address,
|
||||
const SocketAddress& remote_address,
|
||||
const ProxyInfo& proxy_info,
|
||||
const std::string& user_agent,
|
||||
const PacketSocketTcpOptions& tcp_options) override;
|
||||
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface> CreateAsyncDnsResolver()
|
||||
override;
|
||||
|
||||
private:
|
||||
int BindSocket(Socket* socket,
|
||||
const SocketAddress& local_address,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port);
|
||||
|
||||
SocketFactory* socket_factory_;
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif // P2P_BASE_BASIC_PACKET_SOCKET_FACTORY_H_
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_CANDIDATE_PAIR_INTERFACE_H_
|
||||
#define P2P_BASE_CANDIDATE_PAIR_INTERFACE_H_
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class Candidate;
|
||||
|
||||
class CandidatePairInterface {
|
||||
public:
|
||||
virtual ~CandidatePairInterface() {}
|
||||
|
||||
virtual const Candidate& local_candidate() const = 0;
|
||||
virtual const Candidate& remote_candidate() const = 0;
|
||||
};
|
||||
|
||||
// Specific implementation of the interface, suitable for being a
|
||||
// data member of other structs.
|
||||
struct CandidatePair final : public CandidatePairInterface {
|
||||
~CandidatePair() override = default;
|
||||
|
||||
const Candidate& local_candidate() const override { return local; }
|
||||
const Candidate& remote_candidate() const override { return remote; }
|
||||
|
||||
Candidate local;
|
||||
Candidate remote;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_CANDIDATE_PAIR_INTERFACE_H_
|
||||
1823
TMessagesProj/jni/voip/webrtc/p2p/base/connection.cc
Normal file
1823
TMessagesProj/jni/voip/webrtc/p2p/base/connection.cc
Normal file
File diff suppressed because it is too large
Load diff
535
TMessagesProj/jni/voip/webrtc/p2p/base/connection.h
Normal file
535
TMessagesProj/jni/voip/webrtc/p2p/base/connection.h
Normal file
|
|
@ -0,0 +1,535 @@
|
|||
/*
|
||||
* Copyright 2019 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_CONNECTION_H_
|
||||
#define P2P_BASE_CONNECTION_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/functional/any_invocable.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/candidate.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "api/transport/stun.h"
|
||||
#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h"
|
||||
#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h"
|
||||
#include "logging/rtc_event_log/ice_logger.h"
|
||||
#include "p2p/base/candidate_pair_interface.h"
|
||||
#include "p2p/base/connection_info.h"
|
||||
#include "p2p/base/p2p_transport_channel_ice_field_trials.h"
|
||||
#include "p2p/base/port_interface.h"
|
||||
#include "p2p/base/stun_request.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/network.h"
|
||||
#include "rtc_base/network/received_packet.h"
|
||||
#include "rtc_base/numerics/event_based_exponential_moving_average.h"
|
||||
#include "rtc_base/rate_tracker.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
#include "rtc_base/weak_ptr.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// Version number for GOOG_PING, this is added to have the option of
|
||||
// adding other flavors in the future.
|
||||
constexpr int kGoogPingVersion = 1;
|
||||
|
||||
// Forward declaration so that a ConnectionRequest can contain a Connection.
|
||||
class Connection;
|
||||
|
||||
// Represents a communication link between a port on the local client and a
|
||||
// port on the remote client.
|
||||
class RTC_EXPORT Connection : public CandidatePairInterface {
|
||||
public:
|
||||
struct SentPing {
|
||||
SentPing(absl::string_view id, int64_t sent_time, uint32_t nomination)
|
||||
: id(id), sent_time(sent_time), nomination(nomination) {}
|
||||
|
||||
std::string id;
|
||||
int64_t sent_time;
|
||||
uint32_t nomination;
|
||||
};
|
||||
|
||||
~Connection() override;
|
||||
|
||||
// A unique ID assigned when the connection is created.
|
||||
uint32_t id() const { return id_; }
|
||||
|
||||
webrtc::TaskQueueBase* network_thread() const;
|
||||
|
||||
// Implementation of virtual methods in CandidatePairInterface.
|
||||
// Returns the description of the local port
|
||||
const Candidate& local_candidate() const override;
|
||||
// Returns the description of the remote port to which we communicate.
|
||||
const Candidate& remote_candidate() const override;
|
||||
|
||||
// Return local network for this connection.
|
||||
virtual const rtc::Network* network() const;
|
||||
// Return generation for this connection.
|
||||
virtual int generation() const;
|
||||
|
||||
// Returns the pair priority.
|
||||
virtual uint64_t priority() const;
|
||||
|
||||
enum WriteState {
|
||||
STATE_WRITABLE = 0, // we have received ping responses recently
|
||||
STATE_WRITE_UNRELIABLE = 1, // we have had a few ping failures
|
||||
STATE_WRITE_INIT = 2, // we have yet to receive a ping response
|
||||
STATE_WRITE_TIMEOUT = 3, // we have had a large number of ping failures
|
||||
};
|
||||
|
||||
WriteState write_state() const;
|
||||
bool writable() const;
|
||||
bool receiving() const;
|
||||
|
||||
const PortInterface* port() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return port_.get();
|
||||
}
|
||||
|
||||
// Determines whether the connection has finished connecting. This can only
|
||||
// be false for TCP connections.
|
||||
bool connected() const;
|
||||
bool weak() const;
|
||||
bool active() const;
|
||||
bool pending_delete() const { return !port_; }
|
||||
|
||||
// A connection is dead if it can be safely deleted.
|
||||
bool dead(int64_t now) const;
|
||||
|
||||
// Estimate of the round-trip time over this connection.
|
||||
int rtt() const;
|
||||
|
||||
int unwritable_timeout() const;
|
||||
void set_unwritable_timeout(const absl::optional<int>& value_ms);
|
||||
int unwritable_min_checks() const;
|
||||
void set_unwritable_min_checks(const absl::optional<int>& value);
|
||||
int inactive_timeout() const;
|
||||
void set_inactive_timeout(const absl::optional<int>& value);
|
||||
|
||||
// Gets the `ConnectionInfo` stats, where `best_connection` has not been
|
||||
// populated (default value false).
|
||||
ConnectionInfo stats();
|
||||
|
||||
sigslot::signal1<Connection*> SignalStateChange;
|
||||
|
||||
// Sent when the connection has decided that it is no longer of value. It
|
||||
// will delete itself immediately after this call.
|
||||
sigslot::signal1<Connection*> SignalDestroyed;
|
||||
|
||||
// The connection can send and receive packets asynchronously. This matches
|
||||
// the interface of AsyncPacketSocket, which may use UDP or TCP under the
|
||||
// covers.
|
||||
virtual int Send(const void* data,
|
||||
size_t size,
|
||||
const rtc::PacketOptions& options) = 0;
|
||||
|
||||
// Error if Send() returns < 0
|
||||
virtual int GetError() = 0;
|
||||
|
||||
// Register as a recipient of received packets. There can only be one.
|
||||
void RegisterReceivedPacketCallback(
|
||||
absl::AnyInvocable<void(Connection*, const rtc::ReceivedPacket&)>
|
||||
received_packet_callback);
|
||||
void DeregisterReceivedPacketCallback();
|
||||
|
||||
sigslot::signal1<Connection*> SignalReadyToSend;
|
||||
|
||||
// Called when a packet is received on this connection.
|
||||
void OnReadPacket(const rtc::ReceivedPacket& packet);
|
||||
[[deprecated("Pass a rtc::ReceivedPacket")]] void
|
||||
OnReadPacket(const char* data, size_t size, int64_t packet_time_us);
|
||||
|
||||
// Called when the socket is currently able to send.
|
||||
void OnReadyToSend();
|
||||
|
||||
// Called when a connection is determined to be no longer useful to us. We
|
||||
// still keep it around in case the other side wants to use it. But we can
|
||||
// safely stop pinging on it and we can allow it to time out if the other
|
||||
// side stops using it as well.
|
||||
bool pruned() const;
|
||||
void Prune();
|
||||
|
||||
bool use_candidate_attr() const;
|
||||
void set_use_candidate_attr(bool enable);
|
||||
|
||||
void set_nomination(uint32_t value);
|
||||
|
||||
uint32_t remote_nomination() const;
|
||||
// One or several pairs may be nominated based on if Regular or Aggressive
|
||||
// Nomination is used. https://tools.ietf.org/html/rfc5245#section-8
|
||||
// `nominated` is defined both for the controlling or controlled agent based
|
||||
// on if a nomination has been pinged or acknowledged. The controlled agent
|
||||
// gets its `remote_nomination_` set when pinged by the controlling agent with
|
||||
// a nomination value. The controlling agent gets its `acked_nomination_` set
|
||||
// when receiving a response to a nominating ping.
|
||||
bool nominated() const;
|
||||
|
||||
int receiving_timeout() const;
|
||||
void set_receiving_timeout(absl::optional<int> receiving_timeout_ms);
|
||||
|
||||
// Deletes a `Connection` instance is by calling the `DestroyConnection`
|
||||
// method in `Port`.
|
||||
// Note: When the function returns, the object has been deleted.
|
||||
void Destroy();
|
||||
|
||||
// Signals object destruction, releases outstanding references and performs
|
||||
// final logging.
|
||||
// The function will return `true` when shutdown was performed, signals
|
||||
// emitted and outstanding references released. If the function returns
|
||||
// `false`, `Shutdown()` has previously been called.
|
||||
bool Shutdown();
|
||||
|
||||
// Prunes the connection and sets its state to STATE_FAILED,
|
||||
// It will not be used or send pings although it can still receive packets.
|
||||
void FailAndPrune();
|
||||
|
||||
// Checks that the state of this connection is up-to-date. The argument is
|
||||
// the current time, which is compared against various timeouts.
|
||||
void UpdateState(int64_t now);
|
||||
|
||||
void UpdateLocalIceParameters(int component,
|
||||
absl::string_view username_fragment,
|
||||
absl::string_view password);
|
||||
|
||||
// Called when this connection should try checking writability again.
|
||||
int64_t last_ping_sent() const;
|
||||
void Ping(int64_t now,
|
||||
std::unique_ptr<StunByteStringAttribute> delta = nullptr);
|
||||
void ReceivedPingResponse(
|
||||
int rtt,
|
||||
absl::string_view request_id,
|
||||
const absl::optional<uint32_t>& nomination = absl::nullopt);
|
||||
std::unique_ptr<IceMessage> BuildPingRequest(
|
||||
std::unique_ptr<StunByteStringAttribute> delta)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
|
||||
int64_t last_ping_response_received() const;
|
||||
const absl::optional<std::string>& last_ping_id_received() const;
|
||||
|
||||
// Used to check if any STUN ping response has been received.
|
||||
int rtt_samples() const;
|
||||
|
||||
// Called whenever a valid ping is received on this connection. This is
|
||||
// public because the connection intercepts the first ping for us.
|
||||
int64_t last_ping_received() const;
|
||||
|
||||
void ReceivedPing(
|
||||
const absl::optional<std::string>& request_id = absl::nullopt);
|
||||
// Handles the binding request; sends a response if this is a valid request.
|
||||
void HandleStunBindingOrGoogPingRequest(IceMessage* msg);
|
||||
// Handles the piggyback acknowledgement of the lastest connectivity check
|
||||
// that the remote peer has received, if it is indicated in the incoming
|
||||
// connectivity check from the peer.
|
||||
void HandlePiggybackCheckAcknowledgementIfAny(StunMessage* msg);
|
||||
// Timestamp when data was last sent (or attempted to be sent).
|
||||
int64_t last_send_data() const;
|
||||
int64_t last_data_received() const;
|
||||
|
||||
// Debugging description of this connection
|
||||
std::string ToDebugId() const;
|
||||
std::string ToString() const;
|
||||
std::string ToSensitiveString() const;
|
||||
// Structured description of this candidate pair.
|
||||
const webrtc::IceCandidatePairDescription& ToLogDescription();
|
||||
void set_ice_event_log(webrtc::IceEventLog* ice_event_log);
|
||||
|
||||
// Prints pings_since_last_response_ into a string.
|
||||
void PrintPingsSinceLastResponse(std::string* pings, size_t max);
|
||||
|
||||
// `set_selected` is only used for logging in ToString above. The flag is
|
||||
// set true by P2PTransportChannel for its selected candidate pair.
|
||||
// TODO(tommi): Remove `selected()` once not referenced downstream.
|
||||
bool selected() const;
|
||||
void set_selected(bool selected);
|
||||
|
||||
// This signal will be fired if this connection is nominated by the
|
||||
// controlling side.
|
||||
sigslot::signal1<Connection*> SignalNominated;
|
||||
|
||||
IceCandidatePairState state() const;
|
||||
|
||||
int num_pings_sent() const;
|
||||
|
||||
uint32_t ComputeNetworkCost() const;
|
||||
|
||||
// Update the ICE password and/or generation of the remote candidate if the
|
||||
// ufrag in `params` matches the candidate's ufrag, and the
|
||||
// candidate's password and/or ufrag has not been set.
|
||||
void MaybeSetRemoteIceParametersAndGeneration(const IceParameters& params,
|
||||
int generation);
|
||||
|
||||
// If `remote_candidate_` is peer reflexive and is equivalent to
|
||||
// `new_candidate` except the type, update `remote_candidate_` to
|
||||
// `new_candidate`.
|
||||
void MaybeUpdatePeerReflexiveCandidate(const Candidate& new_candidate);
|
||||
|
||||
// Returns the last received time of any data, stun request, or stun
|
||||
// response in milliseconds
|
||||
int64_t last_received() const;
|
||||
// Returns the last time when the connection changed its receiving state.
|
||||
int64_t receiving_unchanged_since() const;
|
||||
|
||||
// Constructs the prflx priority as described in
|
||||
// https://datatracker.ietf.org/doc/html/rfc5245#section-4.1.2.1
|
||||
uint32_t prflx_priority() const;
|
||||
|
||||
bool stable(int64_t now) const;
|
||||
|
||||
// Check if we sent `val` pings without receving a response.
|
||||
bool TooManyOutstandingPings(const absl::optional<int>& val) const;
|
||||
|
||||
// Called by Port when the network cost changes.
|
||||
void SetLocalCandidateNetworkCost(uint16_t cost);
|
||||
|
||||
void SetIceFieldTrials(const IceFieldTrials* field_trials);
|
||||
const rtc::EventBasedExponentialMovingAverage& GetRttEstimate() const {
|
||||
return rtt_estimate_;
|
||||
}
|
||||
|
||||
// Reset the connection to a state of a newly connected.
|
||||
// - STATE_WRITE_INIT
|
||||
// - receving = false
|
||||
// - throw away all pending request
|
||||
// - reset RttEstimate
|
||||
//
|
||||
// Keep the following unchanged:
|
||||
// - connected
|
||||
// - remote_candidate
|
||||
// - statistics
|
||||
//
|
||||
// Does not trigger SignalStateChange
|
||||
void ForgetLearnedState();
|
||||
|
||||
void SendStunBindingResponse(const StunMessage* message);
|
||||
void SendGoogPingResponse(const StunMessage* message);
|
||||
void SendResponseMessage(const StunMessage& response);
|
||||
|
||||
// An accessor for unit tests.
|
||||
PortInterface* PortForTest() { return port_.get(); }
|
||||
const PortInterface* PortForTest() const { return port_.get(); }
|
||||
|
||||
std::unique_ptr<IceMessage> BuildPingRequestForTest() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return BuildPingRequest(nullptr);
|
||||
}
|
||||
|
||||
// Public for unit tests.
|
||||
uint32_t acked_nomination() const;
|
||||
void set_remote_nomination(uint32_t remote_nomination);
|
||||
|
||||
const std::string& remote_password_for_test() const {
|
||||
return remote_candidate().password();
|
||||
}
|
||||
void set_remote_password_for_test(absl::string_view pwd) {
|
||||
remote_candidate_.set_password(pwd);
|
||||
}
|
||||
|
||||
void SetStunDictConsumer(
|
||||
std::function<std::unique_ptr<StunAttribute>(
|
||||
const StunByteStringAttribute*)> goog_delta_consumer,
|
||||
std::function<void(webrtc::RTCErrorOr<const StunUInt64Attribute*>)>
|
||||
goog_delta_ack_consumer) {
|
||||
goog_delta_consumer_ = std::move(goog_delta_consumer);
|
||||
goog_delta_ack_consumer_ = std::move(goog_delta_ack_consumer);
|
||||
}
|
||||
|
||||
void ClearStunDictConsumer() {
|
||||
goog_delta_consumer_ = absl::nullopt;
|
||||
goog_delta_ack_consumer_ = absl::nullopt;
|
||||
}
|
||||
|
||||
protected:
|
||||
// A ConnectionRequest is a simple STUN ping used to determine writability.
|
||||
class ConnectionRequest;
|
||||
|
||||
// Constructs a new connection to the given remote port.
|
||||
Connection(rtc::WeakPtr<PortInterface> port,
|
||||
size_t index,
|
||||
const Candidate& candidate);
|
||||
|
||||
// Called back when StunRequestManager has a stun packet to send
|
||||
void OnSendStunPacket(const void* data, size_t size, StunRequest* req);
|
||||
|
||||
// Callbacks from ConnectionRequest
|
||||
virtual void OnConnectionRequestResponse(StunRequest* req,
|
||||
StunMessage* response);
|
||||
void OnConnectionRequestErrorResponse(ConnectionRequest* req,
|
||||
StunMessage* response)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
void OnConnectionRequestTimeout(ConnectionRequest* req)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
void OnConnectionRequestSent(ConnectionRequest* req)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
|
||||
bool rtt_converged() const;
|
||||
|
||||
// If the response is not received within 2 * RTT, the response is assumed to
|
||||
// be missing.
|
||||
bool missing_responses(int64_t now) const;
|
||||
|
||||
// Changes the state and signals if necessary.
|
||||
void set_write_state(WriteState value);
|
||||
void UpdateReceiving(int64_t now);
|
||||
void set_state(IceCandidatePairState state);
|
||||
void set_connected(bool value);
|
||||
|
||||
// The local port where this connection sends and receives packets.
|
||||
PortInterface* port() { return port_.get(); }
|
||||
|
||||
// NOTE: A pointer to the network thread is held by `port_` so in theory we
|
||||
// shouldn't need to hold on to this pointer here, but rather defer to
|
||||
// port_->thread(). However, some tests delete the classes in the wrong order
|
||||
// so `port_` may be deleted before an instance of this class is deleted.
|
||||
// TODO(tommi): This ^^^ should be fixed.
|
||||
webrtc::TaskQueueBase* const network_thread_;
|
||||
const uint32_t id_;
|
||||
rtc::WeakPtr<PortInterface> port_;
|
||||
Candidate local_candidate_ RTC_GUARDED_BY(network_thread_);
|
||||
Candidate remote_candidate_;
|
||||
|
||||
ConnectionInfo stats_;
|
||||
rtc::RateTracker recv_rate_tracker_;
|
||||
rtc::RateTracker send_rate_tracker_;
|
||||
int64_t last_send_data_ = 0;
|
||||
|
||||
private:
|
||||
// Update the local candidate based on the mapped address attribute.
|
||||
// If the local candidate changed, fires SignalStateChange.
|
||||
void MaybeUpdateLocalCandidate(StunRequest* request, StunMessage* response)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
|
||||
void LogCandidatePairConfig(webrtc::IceCandidatePairConfigType type)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
void LogCandidatePairEvent(webrtc::IceCandidatePairEventType type,
|
||||
uint32_t transaction_id)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
|
||||
// Check if this IceMessage is identical
|
||||
// to last message ack:ed STUN_BINDING_REQUEST.
|
||||
bool ShouldSendGoogPing(const StunMessage* message)
|
||||
RTC_RUN_ON(network_thread_);
|
||||
|
||||
WriteState write_state_ RTC_GUARDED_BY(network_thread_);
|
||||
bool receiving_ RTC_GUARDED_BY(network_thread_);
|
||||
bool connected_ RTC_GUARDED_BY(network_thread_);
|
||||
bool pruned_ RTC_GUARDED_BY(network_thread_);
|
||||
bool selected_ RTC_GUARDED_BY(network_thread_) = false;
|
||||
// By default `use_candidate_attr_` flag will be true,
|
||||
// as we will be using aggressive nomination.
|
||||
// But when peer is ice-lite, this flag "must" be initialized to false and
|
||||
// turn on when connection becomes "best connection".
|
||||
bool use_candidate_attr_ RTC_GUARDED_BY(network_thread_);
|
||||
// Used by the controlling side to indicate that this connection will be
|
||||
// selected for transmission if the peer supports ICE-renomination when this
|
||||
// value is positive. A larger-value indicates that a connection is nominated
|
||||
// later and should be selected by the controlled side with higher precedence.
|
||||
// A zero-value indicates not nominating this connection.
|
||||
uint32_t nomination_ RTC_GUARDED_BY(network_thread_) = 0;
|
||||
// The last nomination that has been acknowledged.
|
||||
uint32_t acked_nomination_ RTC_GUARDED_BY(network_thread_) = 0;
|
||||
// Used by the controlled side to remember the nomination value received from
|
||||
// the controlling side. When the peer does not support ICE re-nomination, its
|
||||
// value will be 1 if the connection has been nominated.
|
||||
uint32_t remote_nomination_ RTC_GUARDED_BY(network_thread_) = 0;
|
||||
|
||||
StunRequestManager requests_ RTC_GUARDED_BY(network_thread_);
|
||||
int rtt_ RTC_GUARDED_BY(network_thread_);
|
||||
int rtt_samples_ RTC_GUARDED_BY(network_thread_) = 0;
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-totalroundtriptime
|
||||
uint64_t total_round_trip_time_ms_ RTC_GUARDED_BY(network_thread_) = 0;
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime
|
||||
absl::optional<uint32_t> current_round_trip_time_ms_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
int64_t last_ping_sent_ RTC_GUARDED_BY(
|
||||
network_thread_); // last time we sent a ping to the other side
|
||||
int64_t last_ping_received_
|
||||
RTC_GUARDED_BY(network_thread_); // last time we received a ping from the
|
||||
// other side
|
||||
int64_t last_data_received_ RTC_GUARDED_BY(network_thread_);
|
||||
int64_t last_ping_response_received_ RTC_GUARDED_BY(network_thread_);
|
||||
int64_t receiving_unchanged_since_ RTC_GUARDED_BY(network_thread_) = 0;
|
||||
std::vector<SentPing> pings_since_last_response_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
// Transaction ID of the last connectivity check received. Null if having not
|
||||
// received a ping yet.
|
||||
absl::optional<std::string> last_ping_id_received_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
absl::optional<int> unwritable_timeout_ RTC_GUARDED_BY(network_thread_);
|
||||
absl::optional<int> unwritable_min_checks_ RTC_GUARDED_BY(network_thread_);
|
||||
absl::optional<int> inactive_timeout_ RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
IceCandidatePairState state_ RTC_GUARDED_BY(network_thread_);
|
||||
// Time duration to switch from receiving to not receiving.
|
||||
absl::optional<int> receiving_timeout_ RTC_GUARDED_BY(network_thread_);
|
||||
const int64_t time_created_ms_ RTC_GUARDED_BY(network_thread_);
|
||||
const int64_t delta_internal_unix_epoch_ms_ RTC_GUARDED_BY(network_thread_);
|
||||
int num_pings_sent_ RTC_GUARDED_BY(network_thread_) = 0;
|
||||
|
||||
absl::optional<webrtc::IceCandidatePairDescription> log_description_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
webrtc::IceEventLog* ice_event_log_ RTC_GUARDED_BY(network_thread_) = nullptr;
|
||||
|
||||
// GOOG_PING_REQUEST is sent in place of STUN_BINDING_REQUEST
|
||||
// if configured via field trial, the remote peer supports it (signaled
|
||||
// in STUN_BINDING) and if the last STUN BINDING is identical to the one
|
||||
// that is about to be sent.
|
||||
absl::optional<bool> remote_support_goog_ping_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
std::unique_ptr<StunMessage> cached_stun_binding_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
const IceFieldTrials* field_trials_;
|
||||
rtc::EventBasedExponentialMovingAverage rtt_estimate_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
absl::optional<std::function<std::unique_ptr<StunAttribute>(
|
||||
const StunByteStringAttribute*)>>
|
||||
goog_delta_consumer_;
|
||||
absl::optional<
|
||||
std::function<void(webrtc::RTCErrorOr<const StunUInt64Attribute*>)>>
|
||||
goog_delta_ack_consumer_;
|
||||
absl::AnyInvocable<void(Connection*, const rtc::ReceivedPacket&)>
|
||||
received_packet_callback_;
|
||||
};
|
||||
|
||||
// ProxyConnection defers all the interesting work to the port.
|
||||
class ProxyConnection : public Connection {
|
||||
public:
|
||||
ProxyConnection(rtc::WeakPtr<PortInterface> port,
|
||||
size_t index,
|
||||
const Candidate& remote_candidate);
|
||||
|
||||
int Send(const void* data,
|
||||
size_t size,
|
||||
const rtc::PacketOptions& options) override;
|
||||
int GetError() override;
|
||||
|
||||
private:
|
||||
int error_ = 0;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_CONNECTION_H_
|
||||
44
TMessagesProj/jni/voip/webrtc/p2p/base/connection_info.cc
Normal file
44
TMessagesProj/jni/voip/webrtc/p2p/base/connection_info.cc
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2019 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/connection_info.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
ConnectionInfo::ConnectionInfo()
|
||||
: best_connection(false),
|
||||
writable(false),
|
||||
receiving(false),
|
||||
timeout(false),
|
||||
rtt(0),
|
||||
sent_discarded_bytes(0),
|
||||
sent_total_bytes(0),
|
||||
sent_bytes_second(0),
|
||||
sent_discarded_packets(0),
|
||||
sent_total_packets(0),
|
||||
sent_ping_requests_total(0),
|
||||
sent_ping_requests_before_first_response(0),
|
||||
sent_ping_responses(0),
|
||||
recv_total_bytes(0),
|
||||
recv_bytes_second(0),
|
||||
packets_received(0),
|
||||
recv_ping_requests(0),
|
||||
recv_ping_responses(0),
|
||||
key(nullptr),
|
||||
state(IceCandidatePairState::WAITING),
|
||||
priority(0),
|
||||
nominated(false),
|
||||
total_round_trip_time_ms(0) {}
|
||||
|
||||
ConnectionInfo::ConnectionInfo(const ConnectionInfo&) = default;
|
||||
|
||||
ConnectionInfo::~ConnectionInfo() = default;
|
||||
|
||||
} // namespace cricket
|
||||
87
TMessagesProj/jni/voip/webrtc/p2p/base/connection_info.h
Normal file
87
TMessagesProj/jni/voip/webrtc/p2p/base/connection_info.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright 2019 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_CONNECTION_INFO_H_
|
||||
#define P2P_BASE_CONNECTION_INFO_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/candidate.h"
|
||||
#include "api/units/timestamp.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// States are from RFC 5245. http://tools.ietf.org/html/rfc5245#section-5.7.4
|
||||
enum class IceCandidatePairState {
|
||||
WAITING = 0, // Check has not been performed, Waiting pair on CL.
|
||||
IN_PROGRESS, // Check has been sent, transaction is in progress.
|
||||
SUCCEEDED, // Check already done, produced a successful result.
|
||||
FAILED, // Check for this connection failed.
|
||||
// According to spec there should also be a frozen state, but nothing is ever
|
||||
// frozen because we have not implemented ICE freezing logic.
|
||||
};
|
||||
|
||||
// Stats that we can return about the connections for a transport channel.
|
||||
// TODO(hta): Rename to ConnectionStats
|
||||
struct ConnectionInfo {
|
||||
ConnectionInfo();
|
||||
ConnectionInfo(const ConnectionInfo&);
|
||||
~ConnectionInfo();
|
||||
|
||||
bool best_connection; // Is this the best connection we have?
|
||||
bool writable; // Has this connection received a STUN response?
|
||||
bool receiving; // Has this connection received anything?
|
||||
bool timeout; // Has this connection timed out?
|
||||
size_t rtt; // The STUN RTT for this connection.
|
||||
size_t sent_discarded_bytes; // Number of outgoing bytes discarded due to
|
||||
// socket errors.
|
||||
size_t sent_total_bytes; // Total bytes sent on this connection. Does not
|
||||
// include discarded bytes.
|
||||
size_t sent_bytes_second; // Bps over the last measurement interval.
|
||||
size_t sent_discarded_packets; // Number of outgoing packets discarded due to
|
||||
// socket errors.
|
||||
size_t sent_total_packets; // Number of total outgoing packets attempted for
|
||||
// sending, including discarded packets.
|
||||
size_t sent_ping_requests_total; // Number of STUN ping request sent.
|
||||
size_t sent_ping_requests_before_first_response; // Number of STUN ping
|
||||
// sent before receiving the first response.
|
||||
size_t sent_ping_responses; // Number of STUN ping response sent.
|
||||
|
||||
size_t recv_total_bytes; // Total bytes received on this connection.
|
||||
size_t recv_bytes_second; // Bps over the last measurement interval.
|
||||
size_t packets_received; // Number of packets that were received.
|
||||
size_t recv_ping_requests; // Number of STUN ping request received.
|
||||
size_t recv_ping_responses; // Number of STUN ping response received.
|
||||
Candidate local_candidate; // The local candidate for this connection.
|
||||
Candidate remote_candidate; // The remote candidate for this connection.
|
||||
void* key; // A static value that identifies this conn.
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-state
|
||||
IceCandidatePairState state;
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-priority
|
||||
uint64_t priority;
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-nominated
|
||||
bool nominated;
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-totalroundtriptime
|
||||
uint64_t total_round_trip_time_ms;
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime
|
||||
absl::optional<uint32_t> current_round_trip_time_ms;
|
||||
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-lastpacketreceivedtimestamp
|
||||
absl::optional<webrtc::Timestamp> last_data_received;
|
||||
absl::optional<webrtc::Timestamp> last_data_sent;
|
||||
};
|
||||
|
||||
// Information about all the candidate pairs of a channel.
|
||||
typedef std::vector<ConnectionInfo> ConnectionInfos;
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_CONNECTION_INFO_H_
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2019 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/default_ice_transport_factory.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "api/make_ref_counted.h"
|
||||
#include "p2p/base/basic_ice_controller.h"
|
||||
#include "p2p/base/ice_controller_factory_interface.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class BasicIceControllerFactory
|
||||
: public cricket::IceControllerFactoryInterface {
|
||||
public:
|
||||
std::unique_ptr<cricket::IceControllerInterface> Create(
|
||||
const cricket::IceControllerFactoryArgs& args) override {
|
||||
return std::make_unique<cricket::BasicIceController>(args);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
DefaultIceTransport::DefaultIceTransport(
|
||||
std::unique_ptr<cricket::P2PTransportChannel> internal)
|
||||
: internal_(std::move(internal)) {}
|
||||
|
||||
DefaultIceTransport::~DefaultIceTransport() {
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<IceTransportInterface>
|
||||
DefaultIceTransportFactory::CreateIceTransport(
|
||||
const std::string& transport_name,
|
||||
int component,
|
||||
IceTransportInit init) {
|
||||
BasicIceControllerFactory factory;
|
||||
init.set_ice_controller_factory(&factory);
|
||||
return rtc::make_ref_counted<DefaultIceTransport>(
|
||||
cricket::P2PTransportChannel::Create(transport_name, component,
|
||||
std::move(init)));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2019 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_DEFAULT_ICE_TRANSPORT_FACTORY_H_
|
||||
#define P2P_BASE_DEFAULT_ICE_TRANSPORT_FACTORY_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "api/ice_transport_interface.h"
|
||||
#include "p2p/base/p2p_transport_channel.h"
|
||||
#include "rtc_base/thread.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// The default ICE transport wraps the implementation of IceTransportInternal
|
||||
// provided by P2PTransportChannel. This default transport is not thread safe
|
||||
// and must be constructed, used and destroyed on the same network thread on
|
||||
// which the internal P2PTransportChannel lives.
|
||||
class DefaultIceTransport : public IceTransportInterface {
|
||||
public:
|
||||
explicit DefaultIceTransport(
|
||||
std::unique_ptr<cricket::P2PTransportChannel> internal);
|
||||
~DefaultIceTransport();
|
||||
|
||||
cricket::IceTransportInternal* internal() override {
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
return internal_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
const SequenceChecker thread_checker_{};
|
||||
std::unique_ptr<cricket::P2PTransportChannel> internal_
|
||||
RTC_GUARDED_BY(thread_checker_);
|
||||
};
|
||||
|
||||
class DefaultIceTransportFactory : public IceTransportFactory {
|
||||
public:
|
||||
DefaultIceTransportFactory() = default;
|
||||
~DefaultIceTransportFactory() = default;
|
||||
|
||||
// Must be called on the network thread and returns a DefaultIceTransport.
|
||||
rtc::scoped_refptr<IceTransportInterface> CreateIceTransport(
|
||||
const std::string& transport_name,
|
||||
int component,
|
||||
IceTransportInit init) override;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // P2P_BASE_DEFAULT_ICE_TRANSPORT_FACTORY_H_
|
||||
877
TMessagesProj/jni/voip/webrtc/p2p/base/dtls_transport.cc
Normal file
877
TMessagesProj/jni/voip/webrtc/p2p/base/dtls_transport.cc
Normal file
|
|
@ -0,0 +1,877 @@
|
|||
/*
|
||||
* Copyright 2011 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/dtls_transport.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/array_view.h"
|
||||
#include "api/dtls_transport_interface.h"
|
||||
#include "api/rtc_event_log/rtc_event_log.h"
|
||||
#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h"
|
||||
#include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h"
|
||||
#include "p2p/base/packet_transport_internal.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/dscp.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/rtc_certificate.h"
|
||||
#include "rtc_base/ssl_stream_adapter.h"
|
||||
#include "rtc_base/stream.h"
|
||||
#include "rtc_base/thread.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// We don't pull the RTP constants from rtputils.h, to avoid a layer violation.
|
||||
static const size_t kDtlsRecordHeaderLen = 13;
|
||||
static const size_t kMaxDtlsPacketLen = 2048;
|
||||
static const size_t kMinRtpPacketLen = 12;
|
||||
|
||||
// Maximum number of pending packets in the queue. Packets are read immediately
|
||||
// after they have been written, so a capacity of "1" is sufficient.
|
||||
//
|
||||
// However, this bug seems to indicate that's not the case: crbug.com/1063834
|
||||
// So, temporarily increasing it to 2 to see if that makes a difference.
|
||||
static const size_t kMaxPendingPackets = 2;
|
||||
|
||||
// Minimum and maximum values for the initial DTLS handshake timeout. We'll pick
|
||||
// an initial timeout based on ICE RTT estimates, but clamp it to this range.
|
||||
static const int kMinHandshakeTimeout = 50;
|
||||
static const int kMaxHandshakeTimeout = 3000;
|
||||
|
||||
static bool IsDtlsPacket(const char* data, size_t len) {
|
||||
const uint8_t* u = reinterpret_cast<const uint8_t*>(data);
|
||||
return (len >= kDtlsRecordHeaderLen && (u[0] > 19 && u[0] < 64));
|
||||
}
|
||||
static bool IsDtlsClientHelloPacket(const char* data, size_t len) {
|
||||
if (!IsDtlsPacket(data, len)) {
|
||||
return false;
|
||||
}
|
||||
const uint8_t* u = reinterpret_cast<const uint8_t*>(data);
|
||||
return len > 17 && u[0] == 22 && u[13] == 1;
|
||||
}
|
||||
static bool IsRtpPacket(const char* data, size_t len) {
|
||||
const uint8_t* u = reinterpret_cast<const uint8_t*>(data);
|
||||
return (len >= kMinRtpPacketLen && (u[0] & 0xC0) == 0x80);
|
||||
}
|
||||
|
||||
StreamInterfaceChannel::StreamInterfaceChannel(
|
||||
IceTransportInternal* ice_transport)
|
||||
: ice_transport_(ice_transport),
|
||||
state_(rtc::SS_OPEN),
|
||||
packets_(kMaxPendingPackets, kMaxDtlsPacketLen) {}
|
||||
|
||||
rtc::StreamResult StreamInterfaceChannel::Read(rtc::ArrayView<uint8_t> buffer,
|
||||
size_t& read,
|
||||
int& error) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
|
||||
if (state_ == rtc::SS_CLOSED)
|
||||
return rtc::SR_EOS;
|
||||
if (state_ == rtc::SS_OPENING)
|
||||
return rtc::SR_BLOCK;
|
||||
|
||||
if (!packets_.ReadFront(buffer.data(), buffer.size(), &read)) {
|
||||
return rtc::SR_BLOCK;
|
||||
}
|
||||
|
||||
return rtc::SR_SUCCESS;
|
||||
}
|
||||
|
||||
rtc::StreamResult StreamInterfaceChannel::Write(
|
||||
rtc::ArrayView<const uint8_t> data,
|
||||
size_t& written,
|
||||
int& error) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
// Always succeeds, since this is an unreliable transport anyway.
|
||||
// TODO(zhihuang): Should this block if ice_transport_'s temporarily
|
||||
// unwritable?
|
||||
rtc::PacketOptions packet_options;
|
||||
ice_transport_->SendPacket(reinterpret_cast<const char*>(data.data()),
|
||||
data.size(), packet_options);
|
||||
written = data.size();
|
||||
return rtc::SR_SUCCESS;
|
||||
}
|
||||
|
||||
bool StreamInterfaceChannel::OnPacketReceived(const char* data, size_t size) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
if (packets_.size() > 0) {
|
||||
RTC_LOG(LS_WARNING) << "Packet already in queue.";
|
||||
}
|
||||
bool ret = packets_.WriteBack(data, size, NULL);
|
||||
if (!ret) {
|
||||
// Somehow we received another packet before the SSLStreamAdapter read the
|
||||
// previous one out of our temporary buffer. In this case, we'll log an
|
||||
// error and still signal the read event, hoping that it will read the
|
||||
// packet currently in packets_.
|
||||
RTC_LOG(LS_ERROR) << "Failed to write packet to queue.";
|
||||
}
|
||||
SignalEvent(this, rtc::SE_READ, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rtc::StreamState StreamInterfaceChannel::GetState() const {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
return state_;
|
||||
}
|
||||
|
||||
void StreamInterfaceChannel::Close() {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
packets_.Clear();
|
||||
state_ = rtc::SS_CLOSED;
|
||||
}
|
||||
|
||||
DtlsTransport::DtlsTransport(IceTransportInternal* ice_transport,
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
webrtc::RtcEventLog* event_log,
|
||||
rtc::SSLProtocolVersion max_version)
|
||||
: component_(ice_transport->component()),
|
||||
ice_transport_(ice_transport),
|
||||
downward_(NULL),
|
||||
srtp_ciphers_(crypto_options.GetSupportedDtlsSrtpCryptoSuites()),
|
||||
ssl_max_version_(max_version),
|
||||
event_log_(event_log) {
|
||||
RTC_DCHECK(ice_transport_);
|
||||
ConnectToIceTransport();
|
||||
}
|
||||
|
||||
DtlsTransport::~DtlsTransport() = default;
|
||||
|
||||
webrtc::DtlsTransportState DtlsTransport::dtls_state() const {
|
||||
return dtls_state_;
|
||||
}
|
||||
|
||||
const std::string& DtlsTransport::transport_name() const {
|
||||
return ice_transport_->transport_name();
|
||||
}
|
||||
|
||||
int DtlsTransport::component() const {
|
||||
return component_;
|
||||
}
|
||||
|
||||
bool DtlsTransport::IsDtlsActive() const {
|
||||
return dtls_active_;
|
||||
}
|
||||
|
||||
bool DtlsTransport::SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
|
||||
if (dtls_active_) {
|
||||
if (certificate == local_certificate_) {
|
||||
// This may happen during renegotiation.
|
||||
RTC_LOG(LS_INFO) << ToString() << ": Ignoring identical DTLS identity";
|
||||
return true;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Can't change DTLS local identity in this state";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (certificate) {
|
||||
local_certificate_ = certificate;
|
||||
dtls_active_ = true;
|
||||
} else {
|
||||
RTC_LOG(LS_INFO) << ToString()
|
||||
<< ": NULL DTLS identity supplied. Not doing DTLS";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> DtlsTransport::GetLocalCertificate()
|
||||
const {
|
||||
return local_certificate_;
|
||||
}
|
||||
|
||||
bool DtlsTransport::SetDtlsRole(rtc::SSLRole role) {
|
||||
if (dtls_) {
|
||||
RTC_DCHECK(dtls_role_);
|
||||
if (*dtls_role_ != role) {
|
||||
RTC_LOG(LS_ERROR)
|
||||
<< "SSL Role can't be reversed after the session is setup.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
dtls_role_ = role;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DtlsTransport::GetDtlsRole(rtc::SSLRole* role) const {
|
||||
if (!dtls_role_) {
|
||||
return false;
|
||||
}
|
||||
*role = *dtls_role_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DtlsTransport::GetSslCipherSuite(int* cipher) {
|
||||
if (dtls_state() != webrtc::DtlsTransportState::kConnected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return dtls_->GetSslCipherSuite(cipher);
|
||||
}
|
||||
|
||||
webrtc::RTCError DtlsTransport::SetRemoteParameters(
|
||||
absl::string_view digest_alg,
|
||||
const uint8_t* digest,
|
||||
size_t digest_len,
|
||||
absl::optional<rtc::SSLRole> role) {
|
||||
rtc::Buffer remote_fingerprint_value(digest, digest_len);
|
||||
bool is_dtls_restart =
|
||||
dtls_active_ && remote_fingerprint_value_ != remote_fingerprint_value;
|
||||
// Set SSL role. Role must be set before fingerprint is applied, which
|
||||
// initiates DTLS setup.
|
||||
if (role) {
|
||||
if (is_dtls_restart) {
|
||||
dtls_role_ = *role;
|
||||
} else {
|
||||
if (!SetDtlsRole(*role)) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to set SSL role for the transport.");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Apply remote fingerprint.
|
||||
if (!SetRemoteFingerprint(digest_alg, digest, digest_len)) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to apply remote fingerprint.");
|
||||
}
|
||||
return webrtc::RTCError::OK();
|
||||
}
|
||||
|
||||
bool DtlsTransport::SetRemoteFingerprint(absl::string_view digest_alg,
|
||||
const uint8_t* digest,
|
||||
size_t digest_len) {
|
||||
rtc::Buffer remote_fingerprint_value(digest, digest_len);
|
||||
|
||||
// Once we have the local certificate, the same remote fingerprint can be set
|
||||
// multiple times.
|
||||
if (dtls_active_ && remote_fingerprint_value_ == remote_fingerprint_value &&
|
||||
!digest_alg.empty()) {
|
||||
// This may happen during renegotiation.
|
||||
RTC_LOG(LS_INFO) << ToString()
|
||||
<< ": Ignoring identical remote DTLS fingerprint";
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the other side doesn't support DTLS, turn off `dtls_active_`.
|
||||
// TODO(deadbeef): Remove this. It's dangerous, because it relies on higher
|
||||
// level code to ensure DTLS is actually used, but there are tests that
|
||||
// depend on it, for the case where an m= section is rejected. In that case
|
||||
// SetRemoteFingerprint shouldn't even be called though.
|
||||
if (digest_alg.empty()) {
|
||||
RTC_DCHECK(!digest_len);
|
||||
RTC_LOG(LS_INFO) << ToString() << ": Other side didn't support DTLS.";
|
||||
dtls_active_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, we must have a local certificate before setting remote
|
||||
// fingerprint.
|
||||
if (!dtls_active_) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Can't set DTLS remote settings in this state.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// At this point we know we are doing DTLS
|
||||
bool fingerprint_changing = remote_fingerprint_value_.size() > 0u;
|
||||
remote_fingerprint_value_ = std::move(remote_fingerprint_value);
|
||||
remote_fingerprint_algorithm_ = std::string(digest_alg);
|
||||
|
||||
if (dtls_ && !fingerprint_changing) {
|
||||
// This can occur if DTLS is set up before a remote fingerprint is
|
||||
// received. For instance, if we set up DTLS due to receiving an early
|
||||
// ClientHello.
|
||||
rtc::SSLPeerCertificateDigestError err;
|
||||
if (!dtls_->SetPeerCertificateDigest(
|
||||
remote_fingerprint_algorithm_,
|
||||
reinterpret_cast<unsigned char*>(remote_fingerprint_value_.data()),
|
||||
remote_fingerprint_value_.size(), &err)) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Couldn't set DTLS certificate digest.";
|
||||
set_dtls_state(webrtc::DtlsTransportState::kFailed);
|
||||
// If the error is "verification failed", don't return false, because
|
||||
// this means the fingerprint was formatted correctly but didn't match
|
||||
// the certificate from the DTLS handshake. Thus the DTLS state should go
|
||||
// to "failed", but SetRemoteDescription shouldn't fail.
|
||||
return err == rtc::SSLPeerCertificateDigestError::VERIFICATION_FAILED;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the fingerprint is changing, we'll tear down the DTLS association and
|
||||
// create a new one, resetting our state.
|
||||
if (dtls_ && fingerprint_changing) {
|
||||
dtls_.reset(nullptr);
|
||||
set_dtls_state(webrtc::DtlsTransportState::kNew);
|
||||
set_writable(false);
|
||||
}
|
||||
|
||||
if (!SetupDtls()) {
|
||||
set_dtls_state(webrtc::DtlsTransportState::kFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<rtc::SSLCertChain> DtlsTransport::GetRemoteSSLCertChain()
|
||||
const {
|
||||
if (!dtls_) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dtls_->GetPeerSSLCertChain();
|
||||
}
|
||||
|
||||
bool DtlsTransport::ExportKeyingMaterial(absl::string_view label,
|
||||
const uint8_t* context,
|
||||
size_t context_len,
|
||||
bool use_context,
|
||||
uint8_t* result,
|
||||
size_t result_len) {
|
||||
return (dtls_.get())
|
||||
? dtls_->ExportKeyingMaterial(label, context, context_len,
|
||||
use_context, result, result_len)
|
||||
: false;
|
||||
}
|
||||
|
||||
bool DtlsTransport::SetupDtls() {
|
||||
RTC_DCHECK(dtls_role_);
|
||||
{
|
||||
auto downward = std::make_unique<StreamInterfaceChannel>(ice_transport_);
|
||||
StreamInterfaceChannel* downward_ptr = downward.get();
|
||||
|
||||
dtls_ = rtc::SSLStreamAdapter::Create(
|
||||
std::move(downward),
|
||||
[this](rtc::SSLHandshakeError error) { OnDtlsHandshakeError(error); });
|
||||
if (!dtls_) {
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Failed to create DTLS adapter.";
|
||||
return false;
|
||||
}
|
||||
downward_ = downward_ptr;
|
||||
}
|
||||
|
||||
dtls_->SetIdentity(local_certificate_->identity()->Clone());
|
||||
dtls_->SetMode(rtc::SSL_MODE_DTLS);
|
||||
dtls_->SetMaxProtocolVersion(ssl_max_version_);
|
||||
dtls_->SetServerRole(*dtls_role_);
|
||||
dtls_->SignalEvent.connect(this, &DtlsTransport::OnDtlsEvent);
|
||||
if (remote_fingerprint_value_.size() &&
|
||||
!dtls_->SetPeerCertificateDigest(
|
||||
remote_fingerprint_algorithm_,
|
||||
reinterpret_cast<unsigned char*>(remote_fingerprint_value_.data()),
|
||||
remote_fingerprint_value_.size())) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Couldn't set DTLS certificate digest.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up DTLS-SRTP, if it's been enabled.
|
||||
if (!srtp_ciphers_.empty()) {
|
||||
if (!dtls_->SetDtlsSrtpCryptoSuites(srtp_ciphers_)) {
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Couldn't set DTLS-SRTP ciphers.";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
RTC_LOG(LS_INFO) << ToString() << ": Not using DTLS-SRTP.";
|
||||
}
|
||||
|
||||
RTC_LOG(LS_INFO) << ToString() << ": DTLS setup complete.";
|
||||
|
||||
// If the underlying ice_transport is already writable at this point, we may
|
||||
// be able to start DTLS right away.
|
||||
MaybeStartDtls();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DtlsTransport::GetSrtpCryptoSuite(int* cipher) {
|
||||
if (dtls_state() != webrtc::DtlsTransportState::kConnected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return dtls_->GetDtlsSrtpCryptoSuite(cipher);
|
||||
}
|
||||
|
||||
bool DtlsTransport::GetSslVersionBytes(int* version) const {
|
||||
if (dtls_state() != webrtc::DtlsTransportState::kConnected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return dtls_->GetSslVersionBytes(version);
|
||||
}
|
||||
|
||||
uint16_t DtlsTransport::GetSslPeerSignatureAlgorithm() const {
|
||||
if (dtls_state() != webrtc::DtlsTransportState::kConnected) {
|
||||
return rtc::kSslSignatureAlgorithmUnknown; // "not applicable"
|
||||
}
|
||||
return dtls_->GetPeerSignatureAlgorithm();
|
||||
}
|
||||
|
||||
// Called from upper layers to send a media packet.
|
||||
int DtlsTransport::SendPacket(const char* data,
|
||||
size_t size,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags) {
|
||||
if (!dtls_active_) {
|
||||
// Not doing DTLS.
|
||||
return ice_transport_->SendPacket(data, size, options);
|
||||
}
|
||||
|
||||
switch (dtls_state()) {
|
||||
case webrtc::DtlsTransportState::kNew:
|
||||
// Can't send data until the connection is active.
|
||||
// TODO(ekr@rtfm.com): assert here if dtls_ is NULL?
|
||||
return -1;
|
||||
case webrtc::DtlsTransportState::kConnecting:
|
||||
// Can't send data until the connection is active.
|
||||
return -1;
|
||||
case webrtc::DtlsTransportState::kConnected:
|
||||
if (flags & PF_SRTP_BYPASS) {
|
||||
RTC_DCHECK(!srtp_ciphers_.empty());
|
||||
if (!IsRtpPacket(data, size)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ice_transport_->SendPacket(data, size, options);
|
||||
} else {
|
||||
size_t written;
|
||||
int error;
|
||||
return (dtls_->WriteAll(
|
||||
rtc::MakeArrayView(reinterpret_cast<const uint8_t*>(data),
|
||||
size),
|
||||
written, error) == rtc::SR_SUCCESS)
|
||||
? static_cast<int>(size)
|
||||
: -1;
|
||||
}
|
||||
case webrtc::DtlsTransportState::kFailed:
|
||||
// Can't send anything when we're failed.
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Couldn't send packet due to "
|
||||
"webrtc::DtlsTransportState::kFailed.";
|
||||
return -1;
|
||||
case webrtc::DtlsTransportState::kClosed:
|
||||
// Can't send anything when we're closed.
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Couldn't send packet due to "
|
||||
"webrtc::DtlsTransportState::kClosed.";
|
||||
return -1;
|
||||
default:
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
IceTransportInternal* DtlsTransport::ice_transport() {
|
||||
return ice_transport_;
|
||||
}
|
||||
|
||||
bool DtlsTransport::IsDtlsConnected() {
|
||||
return dtls_ && dtls_->IsTlsConnected();
|
||||
}
|
||||
|
||||
bool DtlsTransport::receiving() const {
|
||||
return receiving_;
|
||||
}
|
||||
|
||||
bool DtlsTransport::writable() const {
|
||||
return writable_;
|
||||
}
|
||||
|
||||
int DtlsTransport::GetError() {
|
||||
return ice_transport_->GetError();
|
||||
}
|
||||
|
||||
absl::optional<rtc::NetworkRoute> DtlsTransport::network_route() const {
|
||||
return ice_transport_->network_route();
|
||||
}
|
||||
|
||||
bool DtlsTransport::GetOption(rtc::Socket::Option opt, int* value) {
|
||||
return ice_transport_->GetOption(opt, value);
|
||||
}
|
||||
|
||||
int DtlsTransport::SetOption(rtc::Socket::Option opt, int value) {
|
||||
return ice_transport_->SetOption(opt, value);
|
||||
}
|
||||
|
||||
void DtlsTransport::ConnectToIceTransport() {
|
||||
RTC_DCHECK(ice_transport_);
|
||||
ice_transport_->SignalWritableState.connect(this,
|
||||
&DtlsTransport::OnWritableState);
|
||||
ice_transport_->SignalReadPacket.connect(this, &DtlsTransport::OnReadPacket);
|
||||
ice_transport_->SignalSentPacket.connect(this, &DtlsTransport::OnSentPacket);
|
||||
ice_transport_->SignalReadyToSend.connect(this,
|
||||
&DtlsTransport::OnReadyToSend);
|
||||
ice_transport_->SignalReceivingState.connect(
|
||||
this, &DtlsTransport::OnReceivingState);
|
||||
ice_transport_->SignalNetworkRouteChanged.connect(
|
||||
this, &DtlsTransport::OnNetworkRouteChanged);
|
||||
}
|
||||
|
||||
// The state transition logic here is as follows:
|
||||
// (1) If we're not doing DTLS-SRTP, then the state is just the
|
||||
// state of the underlying impl()
|
||||
// (2) If we're doing DTLS-SRTP:
|
||||
// - Prior to the DTLS handshake, the state is neither receiving nor
|
||||
// writable
|
||||
// - When the impl goes writable for the first time we
|
||||
// start the DTLS handshake
|
||||
// - Once the DTLS handshake completes, the state is that of the
|
||||
// impl again
|
||||
void DtlsTransport::OnWritableState(rtc::PacketTransportInternal* transport) {
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
RTC_DCHECK(transport == ice_transport_);
|
||||
RTC_LOG(LS_VERBOSE) << ToString()
|
||||
<< ": ice_transport writable state changed to "
|
||||
<< ice_transport_->writable();
|
||||
|
||||
if (!dtls_active_) {
|
||||
// Not doing DTLS.
|
||||
// Note: SignalWritableState fired by set_writable.
|
||||
set_writable(ice_transport_->writable());
|
||||
return;
|
||||
}
|
||||
|
||||
switch (dtls_state()) {
|
||||
case webrtc::DtlsTransportState::kNew:
|
||||
MaybeStartDtls();
|
||||
break;
|
||||
case webrtc::DtlsTransportState::kConnected:
|
||||
// Note: SignalWritableState fired by set_writable.
|
||||
set_writable(ice_transport_->writable());
|
||||
break;
|
||||
case webrtc::DtlsTransportState::kConnecting:
|
||||
// Do nothing.
|
||||
break;
|
||||
case webrtc::DtlsTransportState::kFailed:
|
||||
// Should not happen. Do nothing.
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": OnWritableState() called in state "
|
||||
"webrtc::DtlsTransportState::kFailed.";
|
||||
break;
|
||||
case webrtc::DtlsTransportState::kClosed:
|
||||
// Should not happen. Do nothing.
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": OnWritableState() called in state "
|
||||
"webrtc::DtlsTransportState::kClosed.";
|
||||
break;
|
||||
case webrtc::DtlsTransportState::kNumValues:
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DtlsTransport::OnReceivingState(rtc::PacketTransportInternal* transport) {
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
RTC_DCHECK(transport == ice_transport_);
|
||||
RTC_LOG(LS_VERBOSE) << ToString()
|
||||
<< ": ice_transport "
|
||||
"receiving state changed to "
|
||||
<< ice_transport_->receiving();
|
||||
if (!dtls_active_ || dtls_state() == webrtc::DtlsTransportState::kConnected) {
|
||||
// Note: SignalReceivingState fired by set_receiving.
|
||||
set_receiving(ice_transport_->receiving());
|
||||
}
|
||||
}
|
||||
|
||||
void DtlsTransport::OnReadPacket(rtc::PacketTransportInternal* transport,
|
||||
const char* data,
|
||||
size_t size,
|
||||
const int64_t& packet_time_us,
|
||||
int flags) {
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
RTC_DCHECK(transport == ice_transport_);
|
||||
RTC_DCHECK(flags == 0);
|
||||
|
||||
if (!dtls_active_) {
|
||||
// Not doing DTLS.
|
||||
SignalReadPacket(this, data, size, packet_time_us, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (dtls_state()) {
|
||||
case webrtc::DtlsTransportState::kNew:
|
||||
if (dtls_) {
|
||||
RTC_LOG(LS_INFO) << ToString()
|
||||
<< ": Packet received before DTLS started.";
|
||||
} else {
|
||||
RTC_LOG(LS_WARNING) << ToString()
|
||||
<< ": Packet received before we know if we are "
|
||||
"doing DTLS or not.";
|
||||
}
|
||||
// Cache a client hello packet received before DTLS has actually started.
|
||||
if (IsDtlsClientHelloPacket(data, size)) {
|
||||
RTC_LOG(LS_INFO) << ToString()
|
||||
<< ": Caching DTLS ClientHello packet until DTLS is "
|
||||
"started.";
|
||||
cached_client_hello_.SetData(data, size);
|
||||
// If we haven't started setting up DTLS yet (because we don't have a
|
||||
// remote fingerprint/role), we can use the client hello as a clue that
|
||||
// the peer has chosen the client role, and proceed with the handshake.
|
||||
// The fingerprint will be verified when it's set.
|
||||
if (!dtls_ && local_certificate_) {
|
||||
SetDtlsRole(rtc::SSL_SERVER);
|
||||
SetupDtls();
|
||||
}
|
||||
} else {
|
||||
RTC_LOG(LS_INFO) << ToString()
|
||||
<< ": Not a DTLS ClientHello packet; dropping.";
|
||||
}
|
||||
break;
|
||||
|
||||
case webrtc::DtlsTransportState::kConnecting:
|
||||
case webrtc::DtlsTransportState::kConnected:
|
||||
// We should only get DTLS or SRTP packets; STUN's already been demuxed.
|
||||
// Is this potentially a DTLS packet?
|
||||
if (IsDtlsPacket(data, size)) {
|
||||
if (!HandleDtlsPacket(data, size)) {
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Failed to handle DTLS packet.";
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Not a DTLS packet; our handshake should be complete by now.
|
||||
if (dtls_state() != webrtc::DtlsTransportState::kConnected) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Received non-DTLS packet before DTLS "
|
||||
"complete.";
|
||||
return;
|
||||
}
|
||||
|
||||
// And it had better be a SRTP packet.
|
||||
if (!IsRtpPacket(data, size)) {
|
||||
RTC_LOG(LS_ERROR)
|
||||
<< ToString() << ": Received unexpected non-DTLS packet.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanity check.
|
||||
RTC_DCHECK(!srtp_ciphers_.empty());
|
||||
|
||||
// Signal this upwards as a bypass packet.
|
||||
SignalReadPacket(this, data, size, packet_time_us, PF_SRTP_BYPASS);
|
||||
}
|
||||
break;
|
||||
case webrtc::DtlsTransportState::kFailed:
|
||||
case webrtc::DtlsTransportState::kClosed:
|
||||
case webrtc::DtlsTransportState::kNumValues:
|
||||
// This shouldn't be happening. Drop the packet.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DtlsTransport::OnSentPacket(rtc::PacketTransportInternal* transport,
|
||||
const rtc::SentPacket& sent_packet) {
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
SignalSentPacket(this, sent_packet);
|
||||
}
|
||||
|
||||
void DtlsTransport::OnReadyToSend(rtc::PacketTransportInternal* transport) {
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (writable()) {
|
||||
SignalReadyToSend(this);
|
||||
}
|
||||
}
|
||||
|
||||
void DtlsTransport::OnDtlsEvent(rtc::StreamInterface* dtls, int sig, int err) {
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
RTC_DCHECK(dtls == dtls_.get());
|
||||
if (sig & rtc::SE_OPEN) {
|
||||
// This is the first time.
|
||||
RTC_LOG(LS_INFO) << ToString() << ": DTLS handshake complete.";
|
||||
if (dtls_->GetState() == rtc::SS_OPEN) {
|
||||
// The check for OPEN shouldn't be necessary but let's make
|
||||
// sure we don't accidentally frob the state if it's closed.
|
||||
set_dtls_state(webrtc::DtlsTransportState::kConnected);
|
||||
set_writable(true);
|
||||
}
|
||||
}
|
||||
if (sig & rtc::SE_READ) {
|
||||
uint8_t buf[kMaxDtlsPacketLen];
|
||||
size_t read;
|
||||
int read_error;
|
||||
rtc::StreamResult ret;
|
||||
// The underlying DTLS stream may have received multiple DTLS records in
|
||||
// one packet, so read all of them.
|
||||
do {
|
||||
ret = dtls_->Read(buf, read, read_error);
|
||||
if (ret == rtc::SR_SUCCESS) {
|
||||
SignalReadPacket(this, reinterpret_cast<const char*>(buf), read,
|
||||
rtc::TimeMicros(), 0);
|
||||
} else if (ret == rtc::SR_EOS) {
|
||||
// Remote peer shut down the association with no error.
|
||||
RTC_LOG(LS_INFO) << ToString() << ": DTLS transport closed by remote";
|
||||
set_writable(false);
|
||||
set_dtls_state(webrtc::DtlsTransportState::kClosed);
|
||||
SignalClosed(this);
|
||||
} else if (ret == rtc::SR_ERROR) {
|
||||
// Remote peer shut down the association with an error.
|
||||
RTC_LOG(LS_INFO)
|
||||
<< ToString()
|
||||
<< ": Closed by remote with DTLS transport error, code="
|
||||
<< read_error;
|
||||
set_writable(false);
|
||||
set_dtls_state(webrtc::DtlsTransportState::kFailed);
|
||||
SignalClosed(this);
|
||||
}
|
||||
} while (ret == rtc::SR_SUCCESS);
|
||||
}
|
||||
if (sig & rtc::SE_CLOSE) {
|
||||
RTC_DCHECK(sig == rtc::SE_CLOSE); // SE_CLOSE should be by itself.
|
||||
set_writable(false);
|
||||
if (!err) {
|
||||
RTC_LOG(LS_INFO) << ToString() << ": DTLS transport closed";
|
||||
set_dtls_state(webrtc::DtlsTransportState::kClosed);
|
||||
} else {
|
||||
RTC_LOG(LS_INFO) << ToString() << ": DTLS transport error, code=" << err;
|
||||
set_dtls_state(webrtc::DtlsTransportState::kFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DtlsTransport::OnNetworkRouteChanged(
|
||||
absl::optional<rtc::NetworkRoute> network_route) {
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
SignalNetworkRouteChanged(network_route);
|
||||
}
|
||||
|
||||
void DtlsTransport::MaybeStartDtls() {
|
||||
if (dtls_ && ice_transport_->writable()) {
|
||||
ConfigureHandshakeTimeout();
|
||||
|
||||
if (dtls_->StartSSL()) {
|
||||
// This should never fail:
|
||||
// Because we are operating in a nonblocking mode and all
|
||||
// incoming packets come in via OnReadPacket(), which rejects
|
||||
// packets in this state, the incoming queue must be empty. We
|
||||
// ignore write errors, thus any errors must be because of
|
||||
// configuration and therefore are our fault.
|
||||
RTC_DCHECK_NOTREACHED() << "StartSSL failed.";
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Couldn't start DTLS handshake";
|
||||
set_dtls_state(webrtc::DtlsTransportState::kFailed);
|
||||
return;
|
||||
}
|
||||
RTC_LOG(LS_INFO) << ToString()
|
||||
<< ": DtlsTransport: Started DTLS handshake active="
|
||||
<< IsDtlsActive();
|
||||
set_dtls_state(webrtc::DtlsTransportState::kConnecting);
|
||||
// Now that the handshake has started, we can process a cached ClientHello
|
||||
// (if one exists).
|
||||
if (cached_client_hello_.size()) {
|
||||
if (*dtls_role_ == rtc::SSL_SERVER) {
|
||||
RTC_LOG(LS_INFO) << ToString()
|
||||
<< ": Handling cached DTLS ClientHello packet.";
|
||||
if (!HandleDtlsPacket(cached_client_hello_.data<char>(),
|
||||
cached_client_hello_.size())) {
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Failed to handle DTLS packet.";
|
||||
}
|
||||
} else {
|
||||
RTC_LOG(LS_WARNING) << ToString()
|
||||
<< ": Discarding cached DTLS ClientHello packet "
|
||||
"because we don't have the server role.";
|
||||
}
|
||||
cached_client_hello_.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called from OnReadPacket when a DTLS packet is received.
|
||||
bool DtlsTransport::HandleDtlsPacket(const char* data, size_t size) {
|
||||
// Sanity check we're not passing junk that
|
||||
// just looks like DTLS.
|
||||
const uint8_t* tmp_data = reinterpret_cast<const uint8_t*>(data);
|
||||
size_t tmp_size = size;
|
||||
while (tmp_size > 0) {
|
||||
if (tmp_size < kDtlsRecordHeaderLen)
|
||||
return false; // Too short for the header
|
||||
|
||||
size_t record_len = (tmp_data[11] << 8) | (tmp_data[12]);
|
||||
if ((record_len + kDtlsRecordHeaderLen) > tmp_size)
|
||||
return false; // Body too short
|
||||
|
||||
tmp_data += record_len + kDtlsRecordHeaderLen;
|
||||
tmp_size -= record_len + kDtlsRecordHeaderLen;
|
||||
}
|
||||
|
||||
// Looks good. Pass to the SIC which ends up being passed to
|
||||
// the DTLS stack.
|
||||
return downward_->OnPacketReceived(data, size);
|
||||
}
|
||||
|
||||
void DtlsTransport::set_receiving(bool receiving) {
|
||||
if (receiving_ == receiving) {
|
||||
return;
|
||||
}
|
||||
receiving_ = receiving;
|
||||
SignalReceivingState(this);
|
||||
}
|
||||
|
||||
void DtlsTransport::set_writable(bool writable) {
|
||||
if (writable_ == writable) {
|
||||
return;
|
||||
}
|
||||
if (event_log_) {
|
||||
event_log_->Log(
|
||||
std::make_unique<webrtc::RtcEventDtlsWritableState>(writable));
|
||||
}
|
||||
RTC_LOG(LS_VERBOSE) << ToString() << ": set_writable to: " << writable;
|
||||
writable_ = writable;
|
||||
if (writable_) {
|
||||
SignalReadyToSend(this);
|
||||
}
|
||||
SignalWritableState(this);
|
||||
}
|
||||
|
||||
void DtlsTransport::set_dtls_state(webrtc::DtlsTransportState state) {
|
||||
if (dtls_state_ == state) {
|
||||
return;
|
||||
}
|
||||
if (event_log_) {
|
||||
event_log_->Log(
|
||||
std::make_unique<webrtc::RtcEventDtlsTransportState>(state));
|
||||
}
|
||||
RTC_LOG(LS_VERBOSE) << ToString() << ": set_dtls_state from:"
|
||||
<< static_cast<int>(dtls_state_) << " to "
|
||||
<< static_cast<int>(state);
|
||||
dtls_state_ = state;
|
||||
SendDtlsState(this, state);
|
||||
}
|
||||
|
||||
void DtlsTransport::OnDtlsHandshakeError(rtc::SSLHandshakeError error) {
|
||||
SendDtlsHandshakeError(error);
|
||||
}
|
||||
|
||||
void DtlsTransport::ConfigureHandshakeTimeout() {
|
||||
RTC_DCHECK(dtls_);
|
||||
absl::optional<int> rtt = ice_transport_->GetRttEstimate();
|
||||
if (rtt) {
|
||||
// Limit the timeout to a reasonable range in case the ICE RTT takes
|
||||
// extreme values.
|
||||
int initial_timeout = std::max(kMinHandshakeTimeout,
|
||||
std::min(kMaxHandshakeTimeout, 2 * (*rtt)));
|
||||
RTC_LOG(LS_INFO) << ToString() << ": configuring DTLS handshake timeout "
|
||||
<< initial_timeout << " based on ICE RTT " << *rtt;
|
||||
|
||||
dtls_->SetInitialRetransmissionTimeout(initial_timeout);
|
||||
} else {
|
||||
RTC_LOG(LS_INFO)
|
||||
<< ToString()
|
||||
<< ": no RTT estimate - using default DTLS handshake timeout";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
270
TMessagesProj/jni/voip/webrtc/p2p/base/dtls_transport.h
Normal file
270
TMessagesProj/jni/voip/webrtc/p2p/base/dtls_transport.h
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Copyright 2011 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_DTLS_TRANSPORT_H_
|
||||
#define P2P_BASE_DTLS_TRANSPORT_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/crypto/crypto_options.h"
|
||||
#include "api/dtls_transport_interface.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "p2p/base/dtls_transport_internal.h"
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
#include "rtc_base/buffer_queue.h"
|
||||
#include "rtc_base/ssl_stream_adapter.h"
|
||||
#include "rtc_base/stream.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
#include "rtc_base/system/no_unique_address.h"
|
||||
|
||||
namespace rtc {
|
||||
class PacketTransportInternal;
|
||||
}
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// A bridge between a packet-oriented/transport-type interface on
|
||||
// the bottom and a StreamInterface on the top.
|
||||
class StreamInterfaceChannel : public rtc::StreamInterface {
|
||||
public:
|
||||
explicit StreamInterfaceChannel(IceTransportInternal* ice_transport);
|
||||
|
||||
StreamInterfaceChannel(const StreamInterfaceChannel&) = delete;
|
||||
StreamInterfaceChannel& operator=(const StreamInterfaceChannel&) = delete;
|
||||
|
||||
// Push in a packet; this gets pulled out from Read().
|
||||
bool OnPacketReceived(const char* data, size_t size);
|
||||
|
||||
// Implementations of StreamInterface
|
||||
rtc::StreamState GetState() const override;
|
||||
void Close() override;
|
||||
rtc::StreamResult Read(rtc::ArrayView<uint8_t> buffer,
|
||||
size_t& read,
|
||||
int& error) override;
|
||||
rtc::StreamResult Write(rtc::ArrayView<const uint8_t> data,
|
||||
size_t& written,
|
||||
int& error) override;
|
||||
|
||||
private:
|
||||
RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker sequence_checker_;
|
||||
IceTransportInternal* const ice_transport_; // owned by DtlsTransport
|
||||
rtc::StreamState state_ RTC_GUARDED_BY(sequence_checker_);
|
||||
rtc::BufferQueue packets_ RTC_GUARDED_BY(sequence_checker_);
|
||||
};
|
||||
|
||||
// This class provides a DTLS SSLStreamAdapter inside a TransportChannel-style
|
||||
// packet-based interface, wrapping an existing TransportChannel instance
|
||||
// (e.g a P2PTransportChannel)
|
||||
// Here's the way this works:
|
||||
//
|
||||
// DtlsTransport {
|
||||
// SSLStreamAdapter* dtls_ {
|
||||
// StreamInterfaceChannel downward_ {
|
||||
// IceTransportInternal* ice_transport_;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// - Data which comes into DtlsTransport from the underlying
|
||||
// ice_transport_ via OnReadPacket() is checked for whether it is DTLS
|
||||
// or not, and if it is, is passed to DtlsTransport::HandleDtlsPacket,
|
||||
// which pushes it into to downward_. dtls_ is listening for events on
|
||||
// downward_, so it immediately calls downward_->Read().
|
||||
//
|
||||
// - Data written to DtlsTransport is passed either to downward_ or directly
|
||||
// to ice_transport_, depending on whether DTLS is negotiated and whether
|
||||
// the flags include PF_SRTP_BYPASS
|
||||
//
|
||||
// - The SSLStreamAdapter writes to downward_->Write() which translates it
|
||||
// into packet writes on ice_transport_.
|
||||
//
|
||||
// This class is not thread safe; all methods must be called on the same thread
|
||||
// as the constructor.
|
||||
class DtlsTransport : public DtlsTransportInternal {
|
||||
public:
|
||||
// `ice_transport` is the ICE transport this DTLS transport is wrapping. It
|
||||
// must outlive this DTLS transport.
|
||||
//
|
||||
// `crypto_options` are the options used for the DTLS handshake. This affects
|
||||
// whether GCM crypto suites are negotiated.
|
||||
//
|
||||
// `event_log` is an optional RtcEventLog for logging state changes. It should
|
||||
// outlive the DtlsTransport.
|
||||
DtlsTransport(
|
||||
IceTransportInternal* ice_transport,
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
webrtc::RtcEventLog* event_log,
|
||||
rtc::SSLProtocolVersion max_version = rtc::SSL_PROTOCOL_DTLS_12);
|
||||
|
||||
~DtlsTransport() override;
|
||||
|
||||
DtlsTransport(const DtlsTransport&) = delete;
|
||||
DtlsTransport& operator=(const DtlsTransport&) = delete;
|
||||
|
||||
webrtc::DtlsTransportState dtls_state() const override;
|
||||
const std::string& transport_name() const override;
|
||||
int component() const override;
|
||||
|
||||
// DTLS is active if a local certificate was set. Otherwise this acts in a
|
||||
// "passthrough" mode, sending packets directly through the underlying ICE
|
||||
// transport.
|
||||
// TODO(deadbeef): Remove this weirdness, and handle it in the upper layers.
|
||||
bool IsDtlsActive() const override;
|
||||
|
||||
// SetLocalCertificate is what makes DTLS active. It must be called before
|
||||
// SetRemoteFinterprint.
|
||||
// TODO(deadbeef): Once DtlsTransport no longer has the concept of being
|
||||
// "active" or not (acting as a passthrough if not active), just require this
|
||||
// certificate on construction or "Start".
|
||||
bool SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override;
|
||||
|
||||
// SetRemoteFingerprint must be called after SetLocalCertificate, and any
|
||||
// other methods like SetDtlsRole. It's what triggers the actual DTLS setup.
|
||||
// TODO(deadbeef): Rename to "Start" like in ORTC?
|
||||
bool SetRemoteFingerprint(absl::string_view digest_alg,
|
||||
const uint8_t* digest,
|
||||
size_t digest_len) override;
|
||||
|
||||
// SetRemoteParameters must be called after SetLocalCertificate.
|
||||
webrtc::RTCError SetRemoteParameters(
|
||||
absl::string_view digest_alg,
|
||||
const uint8_t* digest,
|
||||
size_t digest_len,
|
||||
absl::optional<rtc::SSLRole> role) override;
|
||||
|
||||
// Called to send a packet (via DTLS, if turned on).
|
||||
int SendPacket(const char* data,
|
||||
size_t size,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags) override;
|
||||
|
||||
bool GetOption(rtc::Socket::Option opt, int* value) override;
|
||||
|
||||
// Find out which TLS version was negotiated
|
||||
bool GetSslVersionBytes(int* version) const override;
|
||||
// Find out which DTLS-SRTP cipher was negotiated
|
||||
bool GetSrtpCryptoSuite(int* cipher) override;
|
||||
|
||||
// Find out which signature algorithm was used by the peer. Returns values
|
||||
// from
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-signaturescheme
|
||||
// If not applicable, it returns zero.
|
||||
uint16_t GetSslPeerSignatureAlgorithm() const override;
|
||||
|
||||
bool GetDtlsRole(rtc::SSLRole* role) const override;
|
||||
bool SetDtlsRole(rtc::SSLRole role) override;
|
||||
|
||||
// Find out which DTLS cipher was negotiated
|
||||
bool GetSslCipherSuite(int* cipher) override;
|
||||
|
||||
// Once DTLS has been established, this method retrieves the certificate
|
||||
// chain in use by the remote peer, for use in external identity
|
||||
// verification.
|
||||
std::unique_ptr<rtc::SSLCertChain> GetRemoteSSLCertChain() const override;
|
||||
|
||||
// Once DTLS has established (i.e., this ice_transport is writable), this
|
||||
// method extracts the keys negotiated during the DTLS handshake, for use in
|
||||
// external encryption. DTLS-SRTP uses this to extract the needed SRTP keys.
|
||||
// See the SSLStreamAdapter documentation for info on the specific parameters.
|
||||
bool ExportKeyingMaterial(absl::string_view label,
|
||||
const uint8_t* context,
|
||||
size_t context_len,
|
||||
bool use_context,
|
||||
uint8_t* result,
|
||||
size_t result_len) override;
|
||||
|
||||
IceTransportInternal* ice_transport() override;
|
||||
|
||||
// For informational purposes. Tells if the DTLS handshake has finished.
|
||||
// This may be true even if writable() is false, if the remote fingerprint
|
||||
// has not yet been verified.
|
||||
bool IsDtlsConnected();
|
||||
|
||||
bool receiving() const override;
|
||||
bool writable() const override;
|
||||
|
||||
int GetError() override;
|
||||
|
||||
absl::optional<rtc::NetworkRoute> network_route() const override;
|
||||
|
||||
int SetOption(rtc::Socket::Option opt, int value) override;
|
||||
|
||||
std::string ToString() const {
|
||||
const absl::string_view RECEIVING_ABBREV[2] = {"_", "R"};
|
||||
const absl::string_view WRITABLE_ABBREV[2] = {"_", "W"};
|
||||
rtc::StringBuilder sb;
|
||||
sb << "DtlsTransport[" << transport_name() << "|" << component_ << "|"
|
||||
<< RECEIVING_ABBREV[receiving()] << WRITABLE_ABBREV[writable()] << "]";
|
||||
return sb.Release();
|
||||
}
|
||||
|
||||
private:
|
||||
void ConnectToIceTransport();
|
||||
|
||||
void OnWritableState(rtc::PacketTransportInternal* transport);
|
||||
void OnReadPacket(rtc::PacketTransportInternal* transport,
|
||||
const char* data,
|
||||
size_t size,
|
||||
const int64_t& packet_time_us,
|
||||
int flags);
|
||||
void OnSentPacket(rtc::PacketTransportInternal* transport,
|
||||
const rtc::SentPacket& sent_packet);
|
||||
void OnReadyToSend(rtc::PacketTransportInternal* transport);
|
||||
void OnReceivingState(rtc::PacketTransportInternal* transport);
|
||||
void OnDtlsEvent(rtc::StreamInterface* stream_, int sig, int err);
|
||||
void OnNetworkRouteChanged(absl::optional<rtc::NetworkRoute> network_route);
|
||||
bool SetupDtls();
|
||||
void MaybeStartDtls();
|
||||
bool HandleDtlsPacket(const char* data, size_t size);
|
||||
void OnDtlsHandshakeError(rtc::SSLHandshakeError error);
|
||||
void ConfigureHandshakeTimeout();
|
||||
|
||||
void set_receiving(bool receiving);
|
||||
void set_writable(bool writable);
|
||||
// Sets the DTLS state, signaling if necessary.
|
||||
void set_dtls_state(webrtc::DtlsTransportState state);
|
||||
|
||||
webrtc::SequenceChecker thread_checker_;
|
||||
|
||||
const int component_;
|
||||
webrtc::DtlsTransportState dtls_state_ = webrtc::DtlsTransportState::kNew;
|
||||
// Underlying ice_transport, not owned by this class.
|
||||
IceTransportInternal* const ice_transport_;
|
||||
std::unique_ptr<rtc::SSLStreamAdapter> dtls_; // The DTLS stream
|
||||
StreamInterfaceChannel*
|
||||
downward_; // Wrapper for ice_transport_, owned by dtls_.
|
||||
const std::vector<int> srtp_ciphers_; // SRTP ciphers to use with DTLS.
|
||||
bool dtls_active_ = false;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate_;
|
||||
absl::optional<rtc::SSLRole> dtls_role_;
|
||||
const rtc::SSLProtocolVersion ssl_max_version_;
|
||||
rtc::Buffer remote_fingerprint_value_;
|
||||
std::string remote_fingerprint_algorithm_;
|
||||
|
||||
// Cached DTLS ClientHello packet that was received before we started the
|
||||
// DTLS handshake. This could happen if the hello was received before the
|
||||
// ice transport became writable, or before a remote fingerprint was received.
|
||||
rtc::Buffer cached_client_hello_;
|
||||
|
||||
bool receiving_ = false;
|
||||
bool writable_ = false;
|
||||
|
||||
webrtc::RtcEventLog* const event_log_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_DTLS_TRANSPORT_H_
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2018 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_DTLS_TRANSPORT_FACTORY_H_
|
||||
#define P2P_BASE_DTLS_TRANSPORT_FACTORY_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "p2p/base/dtls_transport_internal.h"
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// This interface is used to create DTLS transports. The external transports
|
||||
// can be injected into the JsepTransportController through it.
|
||||
//
|
||||
// TODO(qingsi): Remove this factory in favor of one that produces
|
||||
// DtlsTransportInterface given by the public API if this is going to be
|
||||
// injectable.
|
||||
class DtlsTransportFactory {
|
||||
public:
|
||||
virtual ~DtlsTransportFactory() = default;
|
||||
|
||||
virtual std::unique_ptr<DtlsTransportInternal> CreateDtlsTransport(
|
||||
IceTransportInternal* ice,
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
rtc::SSLProtocolVersion max_version) = 0;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_DTLS_TRANSPORT_FACTORY_H_
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright 2017 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/dtls_transport_internal.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
DtlsTransportInternal::DtlsTransportInternal() = default;
|
||||
|
||||
DtlsTransportInternal::~DtlsTransportInternal() = default;
|
||||
|
||||
} // namespace cricket
|
||||
163
TMessagesProj/jni/voip/webrtc/p2p/base/dtls_transport_internal.h
Normal file
163
TMessagesProj/jni/voip/webrtc/p2p/base/dtls_transport_internal.h
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_DTLS_TRANSPORT_INTERNAL_H_
|
||||
#define P2P_BASE_DTLS_TRANSPORT_INTERNAL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/crypto/crypto_options.h"
|
||||
#include "api/dtls_transport_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
#include "p2p/base/packet_transport_internal.h"
|
||||
#include "rtc_base/callback_list.h"
|
||||
#include "rtc_base/ssl_certificate.h"
|
||||
#include "rtc_base/ssl_fingerprint.h"
|
||||
#include "rtc_base/ssl_stream_adapter.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
enum PacketFlags {
|
||||
PF_NORMAL = 0x00, // A normal packet.
|
||||
PF_SRTP_BYPASS = 0x01, // An encrypted SRTP packet; bypass any additional
|
||||
// crypto provided by the transport (e.g. DTLS)
|
||||
};
|
||||
|
||||
// DtlsTransportInternal is an internal interface that does DTLS, also
|
||||
// negotiating SRTP crypto suites so that it may be used for DTLS-SRTP.
|
||||
//
|
||||
// Once the public interface is supported,
|
||||
// (https://www.w3.org/TR/webrtc/#rtcdtlstransport-interface)
|
||||
// the DtlsTransportInterface will be split from this class.
|
||||
class DtlsTransportInternal : public rtc::PacketTransportInternal {
|
||||
public:
|
||||
~DtlsTransportInternal() override;
|
||||
|
||||
DtlsTransportInternal(const DtlsTransportInternal&) = delete;
|
||||
DtlsTransportInternal& operator=(const DtlsTransportInternal&) = delete;
|
||||
|
||||
virtual webrtc::DtlsTransportState dtls_state() const = 0;
|
||||
|
||||
virtual int component() const = 0;
|
||||
|
||||
virtual bool IsDtlsActive() const = 0;
|
||||
|
||||
virtual bool GetDtlsRole(rtc::SSLRole* role) const = 0;
|
||||
|
||||
virtual bool SetDtlsRole(rtc::SSLRole role) = 0;
|
||||
|
||||
// Finds out which TLS/DTLS version is running.
|
||||
virtual bool GetSslVersionBytes(int* version) const = 0;
|
||||
// Finds out which DTLS-SRTP cipher was negotiated.
|
||||
// TODO(zhihuang): Remove this once all dependencies implement this.
|
||||
virtual bool GetSrtpCryptoSuite(int* cipher) = 0;
|
||||
|
||||
// Finds out which DTLS cipher was negotiated.
|
||||
// TODO(zhihuang): Remove this once all dependencies implement this.
|
||||
virtual bool GetSslCipherSuite(int* cipher) = 0;
|
||||
|
||||
// Find out which signature algorithm was used by the peer. Returns values
|
||||
// from
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-signaturescheme
|
||||
// If not applicable, it returns zero.
|
||||
virtual uint16_t GetSslPeerSignatureAlgorithm() const = 0;
|
||||
|
||||
// Gets the local RTCCertificate used for DTLS.
|
||||
virtual rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate()
|
||||
const = 0;
|
||||
|
||||
virtual bool SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) = 0;
|
||||
|
||||
// Gets a copy of the remote side's SSL certificate chain.
|
||||
virtual std::unique_ptr<rtc::SSLCertChain> GetRemoteSSLCertChain() const = 0;
|
||||
|
||||
// Allows key material to be extracted for external encryption.
|
||||
virtual bool ExportKeyingMaterial(absl::string_view label,
|
||||
const uint8_t* context,
|
||||
size_t context_len,
|
||||
bool use_context,
|
||||
uint8_t* result,
|
||||
size_t result_len) = 0;
|
||||
|
||||
// Set DTLS remote fingerprint. Must be after local identity set.
|
||||
ABSL_DEPRECATED("Use SetRemoteParameters instead.")
|
||||
virtual bool SetRemoteFingerprint(absl::string_view digest_alg,
|
||||
const uint8_t* digest,
|
||||
size_t digest_len) = 0;
|
||||
|
||||
// Set DTLS remote fingerprint and role. Must be after local identity set.
|
||||
virtual webrtc::RTCError SetRemoteParameters(
|
||||
absl::string_view digest_alg,
|
||||
const uint8_t* digest,
|
||||
size_t digest_len,
|
||||
absl::optional<rtc::SSLRole> role) = 0;
|
||||
|
||||
ABSL_DEPRECATED("Set the max version via construction.")
|
||||
bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Expose the underneath IceTransport.
|
||||
virtual IceTransportInternal* ice_transport() = 0;
|
||||
|
||||
// F: void(DtlsTransportInternal*, const webrtc::DtlsTransportState)
|
||||
template <typename F>
|
||||
void SubscribeDtlsTransportState(F&& callback) {
|
||||
dtls_transport_state_callback_list_.AddReceiver(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void SubscribeDtlsTransportState(const void* id, F&& callback) {
|
||||
dtls_transport_state_callback_list_.AddReceiver(id,
|
||||
std::forward<F>(callback));
|
||||
}
|
||||
// Unsubscribe the subscription with given id.
|
||||
void UnsubscribeDtlsTransportState(const void* id) {
|
||||
dtls_transport_state_callback_list_.RemoveReceivers(id);
|
||||
}
|
||||
|
||||
void SendDtlsState(DtlsTransportInternal* transport,
|
||||
webrtc::DtlsTransportState state) {
|
||||
dtls_transport_state_callback_list_.Send(transport, state);
|
||||
}
|
||||
|
||||
// Emitted whenever the Dtls handshake failed on some transport channel.
|
||||
// F: void(rtc::SSLHandshakeError)
|
||||
template <typename F>
|
||||
void SubscribeDtlsHandshakeError(F&& callback) {
|
||||
dtls_handshake_error_callback_list_.AddReceiver(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
void SendDtlsHandshakeError(rtc::SSLHandshakeError error) {
|
||||
dtls_handshake_error_callback_list_.Send(error);
|
||||
}
|
||||
|
||||
protected:
|
||||
DtlsTransportInternal();
|
||||
|
||||
private:
|
||||
webrtc::CallbackList<const rtc::SSLHandshakeError>
|
||||
dtls_handshake_error_callback_list_;
|
||||
webrtc::CallbackList<DtlsTransportInternal*, const webrtc::DtlsTransportState>
|
||||
dtls_transport_state_callback_list_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_DTLS_TRANSPORT_INTERNAL_H_
|
||||
319
TMessagesProj/jni/voip/webrtc/p2p/base/fake_dtls_transport.h
Normal file
319
TMessagesProj/jni/voip/webrtc/p2p/base/fake_dtls_transport.h
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
* Copyright 2017 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_FAKE_DTLS_TRANSPORT_H_
|
||||
#define P2P_BASE_FAKE_DTLS_TRANSPORT_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/crypto/crypto_options.h"
|
||||
#include "api/dtls_transport_interface.h"
|
||||
#include "p2p/base/dtls_transport_internal.h"
|
||||
#include "p2p/base/fake_ice_transport.h"
|
||||
#include "rtc_base/fake_ssl_identity.h"
|
||||
#include "rtc_base/rtc_certificate.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// Fake DTLS transport which is implemented by wrapping a fake ICE transport.
|
||||
// Doesn't interact directly with fake ICE transport for anything other than
|
||||
// sending packets.
|
||||
class FakeDtlsTransport : public DtlsTransportInternal {
|
||||
public:
|
||||
explicit FakeDtlsTransport(FakeIceTransport* ice_transport)
|
||||
: ice_transport_(ice_transport),
|
||||
transport_name_(ice_transport->transport_name()),
|
||||
component_(ice_transport->component()),
|
||||
dtls_fingerprint_("", nullptr) {
|
||||
RTC_DCHECK(ice_transport_);
|
||||
ice_transport_->SignalReadPacket.connect(
|
||||
this, &FakeDtlsTransport::OnIceTransportReadPacket);
|
||||
ice_transport_->SignalNetworkRouteChanged.connect(
|
||||
this, &FakeDtlsTransport::OnNetworkRouteChanged);
|
||||
}
|
||||
|
||||
explicit FakeDtlsTransport(std::unique_ptr<FakeIceTransport> ice)
|
||||
: owned_ice_transport_(std::move(ice)),
|
||||
transport_name_(owned_ice_transport_->transport_name()),
|
||||
component_(owned_ice_transport_->component()),
|
||||
dtls_fingerprint_("", rtc::ArrayView<const uint8_t>()) {
|
||||
ice_transport_ = owned_ice_transport_.get();
|
||||
ice_transport_->SignalReadPacket.connect(
|
||||
this, &FakeDtlsTransport::OnIceTransportReadPacket);
|
||||
ice_transport_->SignalNetworkRouteChanged.connect(
|
||||
this, &FakeDtlsTransport::OnNetworkRouteChanged);
|
||||
}
|
||||
|
||||
// If this constructor is called, a new fake ICE transport will be created,
|
||||
// and this FakeDtlsTransport will take the ownership.
|
||||
FakeDtlsTransport(const std::string& name, int component)
|
||||
: FakeDtlsTransport(std::make_unique<FakeIceTransport>(name, component)) {
|
||||
}
|
||||
FakeDtlsTransport(const std::string& name,
|
||||
int component,
|
||||
rtc::Thread* network_thread)
|
||||
: FakeDtlsTransport(std::make_unique<FakeIceTransport>(name,
|
||||
component,
|
||||
network_thread)) {}
|
||||
|
||||
~FakeDtlsTransport() override {
|
||||
if (dest_ && dest_->dest_ == this) {
|
||||
dest_->dest_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Get inner fake ICE transport.
|
||||
FakeIceTransport* fake_ice_transport() { return ice_transport_; }
|
||||
|
||||
// If async, will send packets by "Post"-ing to message queue instead of
|
||||
// synchronously "Send"-ing.
|
||||
void SetAsync(bool async) { ice_transport_->SetAsync(async); }
|
||||
void SetAsyncDelay(int delay_ms) { ice_transport_->SetAsyncDelay(delay_ms); }
|
||||
|
||||
// SetWritable, SetReceiving and SetDestination are the main methods that can
|
||||
// be used for testing, to simulate connectivity or lack thereof.
|
||||
void SetWritable(bool writable) {
|
||||
ice_transport_->SetWritable(writable);
|
||||
set_writable(writable);
|
||||
}
|
||||
void SetReceiving(bool receiving) {
|
||||
ice_transport_->SetReceiving(receiving);
|
||||
set_receiving(receiving);
|
||||
}
|
||||
void SetDtlsState(webrtc::DtlsTransportState state) {
|
||||
dtls_state_ = state;
|
||||
SendDtlsState(this, dtls_state_);
|
||||
}
|
||||
|
||||
// Simulates the two DTLS transports connecting to each other.
|
||||
// If `asymmetric` is true this method only affects this FakeDtlsTransport.
|
||||
// If false, it affects `dest` as well.
|
||||
void SetDestination(FakeDtlsTransport* dest, bool asymmetric = false) {
|
||||
if (dest == dest_) {
|
||||
return;
|
||||
}
|
||||
RTC_DCHECK(!dest || !dest_)
|
||||
<< "Changing fake destination from one to another is not supported.";
|
||||
if (dest && !dest_) {
|
||||
// This simulates the DTLS handshake.
|
||||
dest_ = dest;
|
||||
if (local_cert_ && dest_->local_cert_) {
|
||||
do_dtls_ = true;
|
||||
RTC_LOG(LS_INFO) << "FakeDtlsTransport is doing DTLS";
|
||||
} else {
|
||||
do_dtls_ = false;
|
||||
RTC_LOG(LS_INFO) << "FakeDtlsTransport is not doing DTLS";
|
||||
}
|
||||
SetWritable(true);
|
||||
if (!asymmetric) {
|
||||
dest->SetDestination(this, true);
|
||||
}
|
||||
// If the `dtls_role_` is unset, set it to SSL_CLIENT by default.
|
||||
if (!dtls_role_) {
|
||||
dtls_role_ = std::move(rtc::SSL_CLIENT);
|
||||
}
|
||||
SetDtlsState(webrtc::DtlsTransportState::kConnected);
|
||||
ice_transport_->SetDestination(
|
||||
static_cast<FakeIceTransport*>(dest->ice_transport()), asymmetric);
|
||||
} else {
|
||||
// Simulates loss of connectivity, by asymmetrically forgetting dest_.
|
||||
dest_ = nullptr;
|
||||
SetWritable(false);
|
||||
ice_transport_->SetDestination(nullptr, asymmetric);
|
||||
}
|
||||
}
|
||||
|
||||
// Fake DtlsTransportInternal implementation.
|
||||
webrtc::DtlsTransportState dtls_state() const override { return dtls_state_; }
|
||||
const std::string& transport_name() const override { return transport_name_; }
|
||||
int component() const override { return component_; }
|
||||
const rtc::SSLFingerprint& dtls_fingerprint() const {
|
||||
return dtls_fingerprint_;
|
||||
}
|
||||
webrtc::RTCError SetRemoteParameters(absl::string_view alg,
|
||||
const uint8_t* digest,
|
||||
size_t digest_len,
|
||||
absl::optional<rtc::SSLRole> role) {
|
||||
if (role) {
|
||||
SetDtlsRole(*role);
|
||||
}
|
||||
SetRemoteFingerprint(alg, digest, digest_len);
|
||||
return webrtc::RTCError::OK();
|
||||
}
|
||||
bool SetRemoteFingerprint(absl::string_view alg,
|
||||
const uint8_t* digest,
|
||||
size_t digest_len) {
|
||||
dtls_fingerprint_ =
|
||||
rtc::SSLFingerprint(alg, rtc::MakeArrayView(digest, digest_len));
|
||||
return true;
|
||||
}
|
||||
bool SetDtlsRole(rtc::SSLRole role) override {
|
||||
dtls_role_ = std::move(role);
|
||||
return true;
|
||||
}
|
||||
bool GetDtlsRole(rtc::SSLRole* role) const override {
|
||||
if (!dtls_role_) {
|
||||
return false;
|
||||
}
|
||||
*role = *dtls_role_;
|
||||
return true;
|
||||
}
|
||||
bool SetLocalCertificate(
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override {
|
||||
do_dtls_ = true;
|
||||
local_cert_ = certificate;
|
||||
return true;
|
||||
}
|
||||
void SetRemoteSSLCertificate(rtc::FakeSSLCertificate* cert) {
|
||||
remote_cert_ = cert;
|
||||
}
|
||||
bool IsDtlsActive() const override { return do_dtls_; }
|
||||
bool GetSslVersionBytes(int* version) const override {
|
||||
if (!do_dtls_) {
|
||||
return false;
|
||||
}
|
||||
*version = 0x0102;
|
||||
return true;
|
||||
}
|
||||
bool GetSrtpCryptoSuite(int* crypto_suite) override {
|
||||
if (!do_dtls_) {
|
||||
return false;
|
||||
}
|
||||
*crypto_suite = crypto_suite_;
|
||||
return true;
|
||||
}
|
||||
void SetSrtpCryptoSuite(int crypto_suite) { crypto_suite_ = crypto_suite; }
|
||||
|
||||
bool GetSslCipherSuite(int* cipher_suite) override {
|
||||
if (ssl_cipher_suite_) {
|
||||
*cipher_suite = *ssl_cipher_suite_;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void SetSslCipherSuite(absl::optional<int> cipher_suite) {
|
||||
ssl_cipher_suite_ = cipher_suite;
|
||||
}
|
||||
uint16_t GetSslPeerSignatureAlgorithm() const override { return 0; }
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override {
|
||||
return local_cert_;
|
||||
}
|
||||
std::unique_ptr<rtc::SSLCertChain> GetRemoteSSLCertChain() const override {
|
||||
if (!remote_cert_) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_unique<rtc::SSLCertChain>(remote_cert_->Clone());
|
||||
}
|
||||
bool ExportKeyingMaterial(absl::string_view label,
|
||||
const uint8_t* context,
|
||||
size_t context_len,
|
||||
bool use_context,
|
||||
uint8_t* result,
|
||||
size_t result_len) override {
|
||||
if (!do_dtls_) {
|
||||
return false;
|
||||
}
|
||||
memset(result, 0xff, result_len);
|
||||
return true;
|
||||
}
|
||||
void set_ssl_max_protocol_version(rtc::SSLProtocolVersion version) {
|
||||
ssl_max_version_ = version;
|
||||
}
|
||||
rtc::SSLProtocolVersion ssl_max_protocol_version() const {
|
||||
return ssl_max_version_;
|
||||
}
|
||||
|
||||
IceTransportInternal* ice_transport() override { return ice_transport_; }
|
||||
|
||||
// PacketTransportInternal implementation, which passes through to fake ICE
|
||||
// transport for sending actual packets.
|
||||
bool writable() const override { return writable_; }
|
||||
bool receiving() const override { return receiving_; }
|
||||
int SendPacket(const char* data,
|
||||
size_t len,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags) override {
|
||||
// We expect only SRTP packets to be sent through this interface.
|
||||
if (flags != PF_SRTP_BYPASS && flags != 0) {
|
||||
return -1;
|
||||
}
|
||||
return ice_transport_->SendPacket(data, len, options, flags);
|
||||
}
|
||||
int SetOption(rtc::Socket::Option opt, int value) override {
|
||||
return ice_transport_->SetOption(opt, value);
|
||||
}
|
||||
bool GetOption(rtc::Socket::Option opt, int* value) override {
|
||||
return ice_transport_->GetOption(opt, value);
|
||||
}
|
||||
int GetError() override { return ice_transport_->GetError(); }
|
||||
|
||||
absl::optional<rtc::NetworkRoute> network_route() const override {
|
||||
return ice_transport_->network_route();
|
||||
}
|
||||
|
||||
private:
|
||||
void OnIceTransportReadPacket(PacketTransportInternal* ice_,
|
||||
const char* data,
|
||||
size_t len,
|
||||
const int64_t& packet_time_us,
|
||||
int flags) {
|
||||
SignalReadPacket(this, data, len, packet_time_us, flags);
|
||||
}
|
||||
|
||||
void set_receiving(bool receiving) {
|
||||
if (receiving_ == receiving) {
|
||||
return;
|
||||
}
|
||||
receiving_ = receiving;
|
||||
SignalReceivingState(this);
|
||||
}
|
||||
|
||||
void set_writable(bool writable) {
|
||||
if (writable_ == writable) {
|
||||
return;
|
||||
}
|
||||
writable_ = writable;
|
||||
if (writable_) {
|
||||
SignalReadyToSend(this);
|
||||
}
|
||||
SignalWritableState(this);
|
||||
}
|
||||
|
||||
void OnNetworkRouteChanged(absl::optional<rtc::NetworkRoute> network_route) {
|
||||
SignalNetworkRouteChanged(network_route);
|
||||
}
|
||||
|
||||
FakeIceTransport* ice_transport_;
|
||||
std::unique_ptr<FakeIceTransport> owned_ice_transport_;
|
||||
std::string transport_name_;
|
||||
int component_;
|
||||
FakeDtlsTransport* dest_ = nullptr;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> local_cert_;
|
||||
rtc::FakeSSLCertificate* remote_cert_ = nullptr;
|
||||
bool do_dtls_ = false;
|
||||
rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_12;
|
||||
rtc::SSLFingerprint dtls_fingerprint_;
|
||||
absl::optional<rtc::SSLRole> dtls_role_;
|
||||
int crypto_suite_ = rtc::kSrtpAes128CmSha1_80;
|
||||
absl::optional<int> ssl_cipher_suite_;
|
||||
|
||||
webrtc::DtlsTransportState dtls_state_ = webrtc::DtlsTransportState::kNew;
|
||||
|
||||
bool receiving_ = false;
|
||||
bool writable_ = false;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_FAKE_DTLS_TRANSPORT_H_
|
||||
446
TMessagesProj/jni/voip/webrtc/p2p/base/fake_ice_transport.h
Normal file
446
TMessagesProj/jni/voip/webrtc/p2p/base/fake_ice_transport.h
Normal file
|
|
@ -0,0 +1,446 @@
|
|||
/*
|
||||
* Copyright 2017 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_FAKE_ICE_TRANSPORT_H_
|
||||
#define P2P_BASE_FAKE_ICE_TRANSPORT_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/ice_transport_interface.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
#include "rtc_base/task_queue_for_test.h"
|
||||
|
||||
namespace cricket {
|
||||
using ::webrtc::SafeTask;
|
||||
using ::webrtc::TimeDelta;
|
||||
|
||||
// All methods must be called on the network thread (which is either the thread
|
||||
// calling the constructor, or the separate thread explicitly passed to the
|
||||
// constructor).
|
||||
class FakeIceTransport : public IceTransportInternal {
|
||||
public:
|
||||
explicit FakeIceTransport(absl::string_view name,
|
||||
int component,
|
||||
rtc::Thread* network_thread = nullptr)
|
||||
: name_(name),
|
||||
component_(component),
|
||||
network_thread_(network_thread ? network_thread
|
||||
: rtc::Thread::Current()) {
|
||||
RTC_DCHECK(network_thread_);
|
||||
}
|
||||
// Must be called either on the network thread, or after the network thread
|
||||
// has been shut down.
|
||||
~FakeIceTransport() override {
|
||||
if (dest_ && dest_->dest_ == this) {
|
||||
dest_->dest_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// If async, will send packets by "Post"-ing to message queue instead of
|
||||
// synchronously "Send"-ing.
|
||||
void SetAsync(bool async) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
async_ = async;
|
||||
}
|
||||
void SetAsyncDelay(int delay_ms) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
async_delay_ms_ = delay_ms;
|
||||
}
|
||||
|
||||
// SetWritable, SetReceiving and SetDestination are the main methods that can
|
||||
// be used for testing, to simulate connectivity or lack thereof.
|
||||
void SetWritable(bool writable) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
set_writable(writable);
|
||||
}
|
||||
void SetReceiving(bool receiving) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
set_receiving(receiving);
|
||||
}
|
||||
|
||||
// Simulates the two transports connecting to each other.
|
||||
// If `asymmetric` is true this method only affects this FakeIceTransport.
|
||||
// If false, it affects `dest` as well.
|
||||
void SetDestination(FakeIceTransport* dest, bool asymmetric = false) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (dest == dest_) {
|
||||
return;
|
||||
}
|
||||
RTC_DCHECK(!dest || !dest_)
|
||||
<< "Changing fake destination from one to another is not supported.";
|
||||
if (dest) {
|
||||
// This simulates the delivery of candidates.
|
||||
dest_ = dest;
|
||||
set_writable(true);
|
||||
if (!asymmetric) {
|
||||
dest->SetDestination(this, true);
|
||||
}
|
||||
} else {
|
||||
// Simulates loss of connectivity, by asymmetrically forgetting dest_.
|
||||
dest_ = nullptr;
|
||||
set_writable(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SetTransportState(webrtc::IceTransportState state,
|
||||
IceTransportState legacy_state) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
transport_state_ = state;
|
||||
legacy_transport_state_ = legacy_state;
|
||||
SignalIceTransportStateChanged(this);
|
||||
}
|
||||
|
||||
void SetConnectionCount(size_t connection_count) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
size_t old_connection_count = connection_count_;
|
||||
connection_count_ = connection_count;
|
||||
if (connection_count) {
|
||||
had_connection_ = true;
|
||||
}
|
||||
// In this fake transport channel, `connection_count_` determines the
|
||||
// transport state.
|
||||
if (connection_count_ < old_connection_count) {
|
||||
SignalStateChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
void SetCandidatesGatheringComplete() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (gathering_state_ != kIceGatheringComplete) {
|
||||
gathering_state_ = kIceGatheringComplete;
|
||||
SendGatheringStateEvent();
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for accessing ICE config and other things.
|
||||
int receiving_timeout() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return ice_config_.receiving_timeout_or_default();
|
||||
}
|
||||
bool gather_continually() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return ice_config_.gather_continually();
|
||||
}
|
||||
const Candidates& remote_candidates() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return remote_candidates_;
|
||||
}
|
||||
|
||||
// Fake IceTransportInternal implementation.
|
||||
const std::string& transport_name() const override { return name_; }
|
||||
int component() const override { return component_; }
|
||||
uint64_t IceTiebreaker() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return tiebreaker_;
|
||||
}
|
||||
IceMode remote_ice_mode() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return remote_ice_mode_;
|
||||
}
|
||||
const std::string& ice_ufrag() const { return ice_parameters_.ufrag; }
|
||||
const std::string& ice_pwd() const { return ice_parameters_.pwd; }
|
||||
const std::string& remote_ice_ufrag() const {
|
||||
return remote_ice_parameters_.ufrag;
|
||||
}
|
||||
const std::string& remote_ice_pwd() const {
|
||||
return remote_ice_parameters_.pwd;
|
||||
}
|
||||
const IceParameters& ice_parameters() const { return ice_parameters_; }
|
||||
const IceParameters& remote_ice_parameters() const {
|
||||
return remote_ice_parameters_;
|
||||
}
|
||||
|
||||
IceTransportState GetState() const override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (legacy_transport_state_) {
|
||||
return *legacy_transport_state_;
|
||||
}
|
||||
|
||||
if (connection_count_ == 0) {
|
||||
return had_connection_ ? IceTransportState::STATE_FAILED
|
||||
: IceTransportState::STATE_INIT;
|
||||
}
|
||||
|
||||
if (connection_count_ == 1) {
|
||||
return IceTransportState::STATE_COMPLETED;
|
||||
}
|
||||
|
||||
return IceTransportState::STATE_CONNECTING;
|
||||
}
|
||||
|
||||
webrtc::IceTransportState GetIceTransportState() const override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (transport_state_) {
|
||||
return *transport_state_;
|
||||
}
|
||||
|
||||
if (connection_count_ == 0) {
|
||||
return had_connection_ ? webrtc::IceTransportState::kFailed
|
||||
: webrtc::IceTransportState::kNew;
|
||||
}
|
||||
|
||||
if (connection_count_ == 1) {
|
||||
return webrtc::IceTransportState::kCompleted;
|
||||
}
|
||||
|
||||
return webrtc::IceTransportState::kConnected;
|
||||
}
|
||||
|
||||
void SetIceRole(IceRole role) override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
role_ = role;
|
||||
}
|
||||
IceRole GetIceRole() const override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return role_;
|
||||
}
|
||||
void SetIceTiebreaker(uint64_t tiebreaker) override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
tiebreaker_ = tiebreaker;
|
||||
}
|
||||
void SetIceParameters(const IceParameters& ice_params) override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
ice_parameters_ = ice_params;
|
||||
}
|
||||
void SetRemoteIceParameters(const IceParameters& params) override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
remote_ice_parameters_ = params;
|
||||
}
|
||||
|
||||
void SetRemoteIceMode(IceMode mode) override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
remote_ice_mode_ = mode;
|
||||
}
|
||||
|
||||
void MaybeStartGathering() override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (gathering_state_ == kIceGatheringNew) {
|
||||
gathering_state_ = kIceGatheringGathering;
|
||||
SendGatheringStateEvent();
|
||||
}
|
||||
}
|
||||
|
||||
IceGatheringState gathering_state() const override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return gathering_state_;
|
||||
}
|
||||
|
||||
void SetIceConfig(const IceConfig& config) override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
ice_config_ = config;
|
||||
}
|
||||
|
||||
void AddRemoteCandidate(const Candidate& candidate) override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
remote_candidates_.push_back(candidate);
|
||||
}
|
||||
void RemoveRemoteCandidate(const Candidate& candidate) override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
auto it = absl::c_find(remote_candidates_, candidate);
|
||||
if (it == remote_candidates_.end()) {
|
||||
RTC_LOG(LS_INFO) << "Trying to remove a candidate which doesn't exist.";
|
||||
return;
|
||||
}
|
||||
|
||||
remote_candidates_.erase(it);
|
||||
}
|
||||
|
||||
void RemoveAllRemoteCandidates() override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
remote_candidates_.clear();
|
||||
}
|
||||
|
||||
bool GetStats(IceTransportStats* ice_transport_stats) override {
|
||||
CandidateStats candidate_stats;
|
||||
ConnectionInfo candidate_pair_stats;
|
||||
ice_transport_stats->candidate_stats_list.clear();
|
||||
ice_transport_stats->candidate_stats_list.push_back(candidate_stats);
|
||||
ice_transport_stats->connection_infos.clear();
|
||||
ice_transport_stats->connection_infos.push_back(candidate_pair_stats);
|
||||
return true;
|
||||
}
|
||||
|
||||
absl::optional<int> GetRttEstimate() override { return absl::nullopt; }
|
||||
|
||||
const Connection* selected_connection() const override { return nullptr; }
|
||||
absl::optional<const CandidatePair> GetSelectedCandidatePair()
|
||||
const override {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
// Fake PacketTransportInternal implementation.
|
||||
bool writable() const override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return writable_;
|
||||
}
|
||||
bool receiving() const override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return receiving_;
|
||||
}
|
||||
// If combine is enabled, every two consecutive packets to be sent with
|
||||
// "SendPacket" will be combined into one outgoing packet.
|
||||
void combine_outgoing_packets(bool combine) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
combine_outgoing_packets_ = combine;
|
||||
}
|
||||
int SendPacket(const char* data,
|
||||
size_t len,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags) override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (!dest_) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
send_packet_.AppendData(data, len);
|
||||
if (!combine_outgoing_packets_ || send_packet_.size() > len) {
|
||||
rtc::CopyOnWriteBuffer packet(std::move(send_packet_));
|
||||
if (async_) {
|
||||
network_thread_->PostDelayedTask(
|
||||
SafeTask(task_safety_.flag(),
|
||||
[this, packet] {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
FakeIceTransport::SendPacketInternal(packet);
|
||||
}),
|
||||
TimeDelta::Millis(async_delay_ms_));
|
||||
} else {
|
||||
SendPacketInternal(packet);
|
||||
}
|
||||
}
|
||||
rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis());
|
||||
SignalSentPacket(this, sent_packet);
|
||||
return static_cast<int>(len);
|
||||
}
|
||||
|
||||
int SetOption(rtc::Socket::Option opt, int value) override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
socket_options_[opt] = value;
|
||||
return true;
|
||||
}
|
||||
bool GetOption(rtc::Socket::Option opt, int* value) override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
auto it = socket_options_.find(opt);
|
||||
if (it != socket_options_.end()) {
|
||||
*value = it->second;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int GetError() override { return 0; }
|
||||
|
||||
rtc::CopyOnWriteBuffer last_sent_packet() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return last_sent_packet_;
|
||||
}
|
||||
|
||||
absl::optional<rtc::NetworkRoute> network_route() const override {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return network_route_;
|
||||
}
|
||||
void SetNetworkRoute(absl::optional<rtc::NetworkRoute> network_route) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
network_route_ = network_route;
|
||||
SendTask(network_thread_, [this] {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
SignalNetworkRouteChanged(network_route_);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
void set_writable(bool writable)
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(network_thread_) {
|
||||
if (writable_ == writable) {
|
||||
return;
|
||||
}
|
||||
RTC_LOG(LS_INFO) << "Change writable_ to " << writable;
|
||||
writable_ = writable;
|
||||
if (writable_) {
|
||||
SignalReadyToSend(this);
|
||||
}
|
||||
SignalWritableState(this);
|
||||
}
|
||||
|
||||
void set_receiving(bool receiving)
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(network_thread_) {
|
||||
if (receiving_ == receiving) {
|
||||
return;
|
||||
}
|
||||
receiving_ = receiving;
|
||||
SignalReceivingState(this);
|
||||
}
|
||||
|
||||
void SendPacketInternal(const rtc::CopyOnWriteBuffer& packet)
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(network_thread_) {
|
||||
if (dest_) {
|
||||
last_sent_packet_ = packet;
|
||||
dest_->SignalReadPacket(dest_, packet.data<char>(), packet.size(),
|
||||
rtc::TimeMicros(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string name_;
|
||||
const int component_;
|
||||
FakeIceTransport* dest_ RTC_GUARDED_BY(network_thread_) = nullptr;
|
||||
bool async_ RTC_GUARDED_BY(network_thread_) = false;
|
||||
int async_delay_ms_ RTC_GUARDED_BY(network_thread_) = 0;
|
||||
Candidates remote_candidates_ RTC_GUARDED_BY(network_thread_);
|
||||
IceConfig ice_config_ RTC_GUARDED_BY(network_thread_);
|
||||
IceRole role_ RTC_GUARDED_BY(network_thread_) = ICEROLE_UNKNOWN;
|
||||
uint64_t tiebreaker_ RTC_GUARDED_BY(network_thread_) = 0;
|
||||
IceParameters ice_parameters_ RTC_GUARDED_BY(network_thread_);
|
||||
IceParameters remote_ice_parameters_ RTC_GUARDED_BY(network_thread_);
|
||||
IceMode remote_ice_mode_ RTC_GUARDED_BY(network_thread_) = ICEMODE_FULL;
|
||||
size_t connection_count_ RTC_GUARDED_BY(network_thread_) = 0;
|
||||
absl::optional<webrtc::IceTransportState> transport_state_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
absl::optional<IceTransportState> legacy_transport_state_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
IceGatheringState gathering_state_ RTC_GUARDED_BY(network_thread_) =
|
||||
kIceGatheringNew;
|
||||
bool had_connection_ RTC_GUARDED_BY(network_thread_) = false;
|
||||
bool writable_ RTC_GUARDED_BY(network_thread_) = false;
|
||||
bool receiving_ RTC_GUARDED_BY(network_thread_) = false;
|
||||
bool combine_outgoing_packets_ RTC_GUARDED_BY(network_thread_) = false;
|
||||
rtc::CopyOnWriteBuffer send_packet_ RTC_GUARDED_BY(network_thread_);
|
||||
absl::optional<rtc::NetworkRoute> network_route_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
std::map<rtc::Socket::Option, int> socket_options_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
rtc::CopyOnWriteBuffer last_sent_packet_ RTC_GUARDED_BY(network_thread_);
|
||||
rtc::Thread* const network_thread_;
|
||||
webrtc::ScopedTaskSafetyDetached task_safety_;
|
||||
};
|
||||
|
||||
class FakeIceTransportWrapper : public webrtc::IceTransportInterface {
|
||||
public:
|
||||
explicit FakeIceTransportWrapper(
|
||||
std::unique_ptr<cricket::FakeIceTransport> internal)
|
||||
: internal_(std::move(internal)) {}
|
||||
|
||||
cricket::IceTransportInternal* internal() override { return internal_.get(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<cricket::FakeIceTransport> internal_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_FAKE_ICE_TRANSPORT_H_
|
||||
143
TMessagesProj/jni/voip/webrtc/p2p/base/fake_packet_transport.h
Normal file
143
TMessagesProj/jni/voip/webrtc/p2p/base/fake_packet_transport.h
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright 2017 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_FAKE_PACKET_TRANSPORT_H_
|
||||
#define P2P_BASE_FAKE_PACKET_TRANSPORT_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "p2p/base/packet_transport_internal.h"
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
|
||||
namespace rtc {
|
||||
|
||||
// Used to simulate a packet-based transport.
|
||||
class FakePacketTransport : public PacketTransportInternal {
|
||||
public:
|
||||
explicit FakePacketTransport(const std::string& transport_name)
|
||||
: transport_name_(transport_name) {}
|
||||
~FakePacketTransport() override {
|
||||
if (dest_ && dest_->dest_ == this) {
|
||||
dest_->dest_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// SetWritable, SetReceiving and SetDestination are the main methods that can
|
||||
// be used for testing, to simulate connectivity or lack thereof.
|
||||
void SetWritable(bool writable) { set_writable(writable); }
|
||||
void SetReceiving(bool receiving) { set_receiving(receiving); }
|
||||
|
||||
// Simulates the two transports connecting to each other.
|
||||
// If `asymmetric` is true this method only affects this FakePacketTransport.
|
||||
// If false, it affects `dest` as well.
|
||||
void SetDestination(FakePacketTransport* dest, bool asymmetric) {
|
||||
if (dest) {
|
||||
dest_ = dest;
|
||||
set_writable(true);
|
||||
if (!asymmetric) {
|
||||
dest->SetDestination(this, true);
|
||||
}
|
||||
} else {
|
||||
// Simulates loss of connectivity, by asymmetrically forgetting dest_.
|
||||
dest_ = nullptr;
|
||||
set_writable(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Fake PacketTransportInternal implementation.
|
||||
const std::string& transport_name() const override { return transport_name_; }
|
||||
bool writable() const override { return writable_; }
|
||||
bool receiving() const override { return receiving_; }
|
||||
int SendPacket(const char* data,
|
||||
size_t len,
|
||||
const PacketOptions& options,
|
||||
int flags) override {
|
||||
if (!dest_) {
|
||||
return -1;
|
||||
}
|
||||
CopyOnWriteBuffer packet(data, len);
|
||||
SendPacketInternal(packet);
|
||||
|
||||
SentPacket sent_packet(options.packet_id, TimeMillis());
|
||||
SignalSentPacket(this, sent_packet);
|
||||
return static_cast<int>(len);
|
||||
}
|
||||
|
||||
int SetOption(Socket::Option opt, int value) override {
|
||||
options_[opt] = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool GetOption(Socket::Option opt, int* value) override {
|
||||
auto it = options_.find(opt);
|
||||
if (it == options_.end()) {
|
||||
return false;
|
||||
}
|
||||
*value = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
int GetError() override { return error_; }
|
||||
void SetError(int error) { error_ = error; }
|
||||
|
||||
const CopyOnWriteBuffer* last_sent_packet() { return &last_sent_packet_; }
|
||||
|
||||
absl::optional<NetworkRoute> network_route() const override {
|
||||
return network_route_;
|
||||
}
|
||||
void SetNetworkRoute(absl::optional<NetworkRoute> network_route) {
|
||||
network_route_ = network_route;
|
||||
SignalNetworkRouteChanged(network_route);
|
||||
}
|
||||
|
||||
private:
|
||||
void set_writable(bool writable) {
|
||||
if (writable_ == writable) {
|
||||
return;
|
||||
}
|
||||
writable_ = writable;
|
||||
if (writable_) {
|
||||
SignalReadyToSend(this);
|
||||
}
|
||||
SignalWritableState(this);
|
||||
}
|
||||
|
||||
void set_receiving(bool receiving) {
|
||||
if (receiving_ == receiving) {
|
||||
return;
|
||||
}
|
||||
receiving_ = receiving;
|
||||
SignalReceivingState(this);
|
||||
}
|
||||
|
||||
void SendPacketInternal(const CopyOnWriteBuffer& packet) {
|
||||
last_sent_packet_ = packet;
|
||||
if (dest_) {
|
||||
dest_->SignalReadPacket(dest_, packet.data<char>(), packet.size(),
|
||||
TimeMicros(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
CopyOnWriteBuffer last_sent_packet_;
|
||||
std::string transport_name_;
|
||||
FakePacketTransport* dest_ = nullptr;
|
||||
bool writable_ = false;
|
||||
bool receiving_ = false;
|
||||
|
||||
std::map<Socket::Option, int> options_;
|
||||
int error_ = 0;
|
||||
|
||||
absl::optional<NetworkRoute> network_route_;
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif // P2P_BASE_FAKE_PACKET_TRANSPORT_H_
|
||||
282
TMessagesProj/jni/voip/webrtc/p2p/base/fake_port_allocator.h
Normal file
282
TMessagesProj/jni/voip/webrtc/p2p/base/fake_port_allocator.h
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Copyright 2010 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_FAKE_PORT_ALLOCATOR_H_
|
||||
#define P2P_BASE_FAKE_PORT_ALLOCATOR_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "p2p/base/basic_packet_socket_factory.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "p2p/base/udp_port.h"
|
||||
#include "rtc_base/memory/always_valid_pointer.h"
|
||||
#include "rtc_base/net_helpers.h"
|
||||
#include "rtc_base/net_test_helpers.h"
|
||||
#include "rtc_base/task_queue_for_test.h"
|
||||
#include "rtc_base/thread.h"
|
||||
|
||||
namespace rtc {
|
||||
class SocketFactory;
|
||||
}
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class TestUDPPort : public UDPPort {
|
||||
public:
|
||||
static TestUDPPort* Create(rtc::Thread* thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
bool emit_localhost_for_anyaddress,
|
||||
const webrtc::FieldTrialsView* field_trials) {
|
||||
TestUDPPort* port =
|
||||
new TestUDPPort(thread, factory, network, min_port, max_port, username,
|
||||
password, emit_localhost_for_anyaddress, field_trials);
|
||||
if (!port->Init()) {
|
||||
delete port;
|
||||
port = nullptr;
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
protected:
|
||||
TestUDPPort(rtc::Thread* thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
bool emit_localhost_for_anyaddress,
|
||||
const webrtc::FieldTrialsView* field_trials)
|
||||
: UDPPort(thread,
|
||||
LOCAL_PORT_TYPE,
|
||||
factory,
|
||||
network,
|
||||
min_port,
|
||||
max_port,
|
||||
username,
|
||||
password,
|
||||
emit_localhost_for_anyaddress,
|
||||
field_trials) {}
|
||||
};
|
||||
|
||||
// A FakePortAllocatorSession can be used with either a real or fake socket
|
||||
// factory. It gathers a single loopback port, using IPv6 if available and
|
||||
// not disabled.
|
||||
class FakePortAllocatorSession : public PortAllocatorSession {
|
||||
public:
|
||||
FakePortAllocatorSession(PortAllocator* allocator,
|
||||
rtc::Thread* network_thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
absl::string_view content_name,
|
||||
int component,
|
||||
absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd,
|
||||
const webrtc::FieldTrialsView* field_trials)
|
||||
: PortAllocatorSession(content_name,
|
||||
component,
|
||||
ice_ufrag,
|
||||
ice_pwd,
|
||||
allocator->flags()),
|
||||
allocator_(allocator),
|
||||
network_thread_(network_thread),
|
||||
factory_(factory),
|
||||
ipv4_network_("network",
|
||||
"unittest",
|
||||
rtc::IPAddress(INADDR_LOOPBACK),
|
||||
32),
|
||||
ipv6_network_("network",
|
||||
"unittest",
|
||||
rtc::IPAddress(in6addr_loopback),
|
||||
64),
|
||||
port_(),
|
||||
port_config_count_(0),
|
||||
stun_servers_(allocator->stun_servers()),
|
||||
turn_servers_(allocator->turn_servers()),
|
||||
field_trials_(field_trials) {
|
||||
ipv4_network_.AddIP(rtc::IPAddress(INADDR_LOOPBACK));
|
||||
ipv6_network_.AddIP(rtc::IPAddress(in6addr_loopback));
|
||||
}
|
||||
|
||||
void SetCandidateFilter(uint32_t filter) override {
|
||||
candidate_filter_ = filter;
|
||||
}
|
||||
|
||||
void StartGettingPorts() override {
|
||||
if (!port_) {
|
||||
rtc::Network& network =
|
||||
(rtc::HasIPv6Enabled() && (flags() & PORTALLOCATOR_ENABLE_IPV6))
|
||||
? ipv6_network_
|
||||
: ipv4_network_;
|
||||
port_.reset(TestUDPPort::Create(network_thread_, factory_, &network, 0, 0,
|
||||
username(), password(), false,
|
||||
field_trials_));
|
||||
RTC_DCHECK(port_);
|
||||
port_->SetIceTiebreaker(allocator_->ice_tiebreaker());
|
||||
port_->SubscribePortDestroyed(
|
||||
[this](PortInterface* port) { OnPortDestroyed(port); });
|
||||
AddPort(port_.get());
|
||||
}
|
||||
++port_config_count_;
|
||||
running_ = true;
|
||||
}
|
||||
|
||||
void StopGettingPorts() override { running_ = false; }
|
||||
bool IsGettingPorts() override { return running_; }
|
||||
void ClearGettingPorts() override { is_cleared = true; }
|
||||
bool IsCleared() const override { return is_cleared; }
|
||||
|
||||
void RegatherOnFailedNetworks() override {
|
||||
SignalIceRegathering(this, IceRegatheringReason::NETWORK_FAILURE);
|
||||
}
|
||||
|
||||
std::vector<PortInterface*> ReadyPorts() const override {
|
||||
return ready_ports_;
|
||||
}
|
||||
std::vector<Candidate> ReadyCandidates() const override {
|
||||
return candidates_;
|
||||
}
|
||||
void PruneAllPorts() override { port_->Prune(); }
|
||||
bool CandidatesAllocationDone() const override { return allocation_done_; }
|
||||
|
||||
int port_config_count() { return port_config_count_; }
|
||||
|
||||
const ServerAddresses& stun_servers() const { return stun_servers_; }
|
||||
|
||||
const std::vector<RelayServerConfig>& turn_servers() const {
|
||||
return turn_servers_;
|
||||
}
|
||||
|
||||
uint32_t candidate_filter() const { return candidate_filter_; }
|
||||
|
||||
int transport_info_update_count() const {
|
||||
return transport_info_update_count_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void UpdateIceParametersInternal() override {
|
||||
// Since this class is a fake and this method only is overridden for tests,
|
||||
// we don't need to actually update the transport info.
|
||||
++transport_info_update_count_;
|
||||
}
|
||||
|
||||
private:
|
||||
void AddPort(cricket::Port* port) {
|
||||
port->set_component(component());
|
||||
port->set_generation(generation());
|
||||
port->SignalPortComplete.connect(this,
|
||||
&FakePortAllocatorSession::OnPortComplete);
|
||||
port->PrepareAddress();
|
||||
ready_ports_.push_back(port);
|
||||
SignalPortReady(this, port);
|
||||
port->KeepAliveUntilPruned();
|
||||
}
|
||||
void OnPortComplete(cricket::Port* port) {
|
||||
const std::vector<Candidate>& candidates = port->Candidates();
|
||||
candidates_.insert(candidates_.end(), candidates.begin(), candidates.end());
|
||||
SignalCandidatesReady(this, candidates);
|
||||
|
||||
allocation_done_ = true;
|
||||
SignalCandidatesAllocationDone(this);
|
||||
}
|
||||
void OnPortDestroyed(cricket::PortInterface* port) {
|
||||
// Don't want to double-delete port if it deletes itself.
|
||||
port_.release();
|
||||
}
|
||||
|
||||
PortAllocator* allocator_;
|
||||
rtc::Thread* network_thread_;
|
||||
rtc::PacketSocketFactory* factory_;
|
||||
rtc::Network ipv4_network_;
|
||||
rtc::Network ipv6_network_;
|
||||
std::unique_ptr<cricket::Port> port_;
|
||||
int port_config_count_;
|
||||
std::vector<Candidate> candidates_;
|
||||
std::vector<PortInterface*> ready_ports_;
|
||||
bool allocation_done_ = false;
|
||||
bool is_cleared = false;
|
||||
ServerAddresses stun_servers_;
|
||||
std::vector<RelayServerConfig> turn_servers_;
|
||||
uint32_t candidate_filter_ = CF_ALL;
|
||||
int transport_info_update_count_ = 0;
|
||||
bool running_ = false;
|
||||
const webrtc::FieldTrialsView* field_trials_;
|
||||
};
|
||||
|
||||
class FakePortAllocator : public cricket::PortAllocator {
|
||||
public:
|
||||
FakePortAllocator(rtc::Thread* network_thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
webrtc::FieldTrialsView* field_trials)
|
||||
: FakePortAllocator(network_thread, factory, nullptr, field_trials) {}
|
||||
|
||||
FakePortAllocator(rtc::Thread* network_thread,
|
||||
std::unique_ptr<rtc::PacketSocketFactory> factory,
|
||||
webrtc::FieldTrialsView* field_trials)
|
||||
: FakePortAllocator(network_thread,
|
||||
nullptr,
|
||||
std::move(factory),
|
||||
field_trials) {}
|
||||
|
||||
void SetNetworkIgnoreMask(int network_ignore_mask) override {}
|
||||
|
||||
cricket::PortAllocatorSession* CreateSessionInternal(
|
||||
absl::string_view content_name,
|
||||
int component,
|
||||
absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd) override {
|
||||
return new FakePortAllocatorSession(
|
||||
this, network_thread_, factory_.get(), std::string(content_name),
|
||||
component, std::string(ice_ufrag), std::string(ice_pwd), field_trials_);
|
||||
}
|
||||
|
||||
bool initialized() const { return initialized_; }
|
||||
|
||||
// For testing: Manipulate MdnsObfuscationEnabled()
|
||||
bool MdnsObfuscationEnabled() const override {
|
||||
return mdns_obfuscation_enabled_;
|
||||
}
|
||||
void SetMdnsObfuscationEnabledForTesting(bool enabled) {
|
||||
mdns_obfuscation_enabled_ = enabled;
|
||||
}
|
||||
|
||||
private:
|
||||
FakePortAllocator(rtc::Thread* network_thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
std::unique_ptr<rtc::PacketSocketFactory> owned_factory,
|
||||
webrtc::FieldTrialsView* field_trials)
|
||||
: network_thread_(network_thread),
|
||||
factory_(std::move(owned_factory), factory),
|
||||
field_trials_(field_trials) {
|
||||
if (network_thread_ == nullptr) {
|
||||
network_thread_ = rtc::Thread::Current();
|
||||
Initialize();
|
||||
return;
|
||||
}
|
||||
SendTask(network_thread_, [this] { Initialize(); });
|
||||
}
|
||||
|
||||
rtc::Thread* network_thread_;
|
||||
const webrtc::AlwaysValidPointerNoDefault<rtc::PacketSocketFactory> factory_;
|
||||
const webrtc::FieldTrialsView* field_trials_;
|
||||
bool mdns_obfuscation_enabled_ = false;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_FAKE_PORT_ALLOCATOR_H_
|
||||
80
TMessagesProj/jni/voip/webrtc/p2p/base/ice_agent_interface.h
Normal file
80
TMessagesProj/jni/voip/webrtc/p2p/base/ice_agent_interface.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright 2022 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_ICE_AGENT_INTERFACE_H_
|
||||
#define P2P_BASE_ICE_AGENT_INTERFACE_H_
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "p2p/base/connection.h"
|
||||
#include "p2p/base/ice_switch_reason.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// IceAgentInterface provides methods that allow an ICE controller to manipulate
|
||||
// the connections available to a transport, and used by the transport to
|
||||
// transfer data.
|
||||
class IceAgentInterface {
|
||||
public:
|
||||
virtual ~IceAgentInterface() = default;
|
||||
|
||||
// Get the time when the last ping was sent.
|
||||
// This is only needed in some scenarios if the agent decides to ping on its
|
||||
// own, eg. in some switchover scenarios. Otherwise the ICE controller could
|
||||
// keep this state on its own.
|
||||
// TODO(bugs.webrtc.org/14367): route extra pings through the ICE controller.
|
||||
virtual int64_t GetLastPingSentMs() const = 0;
|
||||
|
||||
// Get the ICE role of this ICE agent.
|
||||
virtual IceRole GetIceRole() const = 0;
|
||||
|
||||
// Called when a pingable connection first becomes available.
|
||||
virtual void OnStartedPinging() = 0;
|
||||
|
||||
// Update the state of all available connections.
|
||||
virtual void UpdateConnectionStates() = 0;
|
||||
|
||||
// Update the internal state of the ICE agent. An ICE controller should call
|
||||
// this at the end of a sequence of actions to combine several mutations into
|
||||
// a single state refresh.
|
||||
// TODO(bugs.webrtc.org/14431): ICE agent state updates should be internal to
|
||||
// the agent. If batching is necessary, use a more appropriate interface.
|
||||
virtual void UpdateState() = 0;
|
||||
|
||||
// Reset the given connections to a state of newly connected connections.
|
||||
// - STATE_WRITE_INIT
|
||||
// - receving = false
|
||||
// - throw away all pending request
|
||||
// - reset RttEstimate
|
||||
//
|
||||
// Keep the following unchanged:
|
||||
// - connected
|
||||
// - remote_candidate
|
||||
// - statistics
|
||||
//
|
||||
// SignalStateChange will not be triggered.
|
||||
virtual void ForgetLearnedStateForConnections(
|
||||
rtc::ArrayView<const Connection* const> connections) = 0;
|
||||
|
||||
// Send a STUN ping request for the given connection.
|
||||
virtual void SendPingRequest(const Connection* connection) = 0;
|
||||
|
||||
// Switch the transport to use the given connection.
|
||||
virtual void SwitchSelectedConnection(const Connection* new_connection,
|
||||
IceSwitchReason reason) = 0;
|
||||
|
||||
// Prune away the given connections. Returns true if pruning is permitted and
|
||||
// successfully performed.
|
||||
virtual bool PruneConnections(
|
||||
rtc::ArrayView<const Connection* const> connections) = 0;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_ICE_AGENT_INTERFACE_H_
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2019 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_ICE_CONTROLLER_FACTORY_INTERFACE_H_
|
||||
#define P2P_BASE_ICE_CONTROLLER_FACTORY_INTERFACE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "p2p/base/ice_controller_interface.h"
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// struct with arguments to IceControllerFactoryInterface::Create
|
||||
struct IceControllerFactoryArgs {
|
||||
std::function<IceTransportState()> ice_transport_state_func;
|
||||
std::function<IceRole()> ice_role_func;
|
||||
std::function<bool(const Connection*)> is_connection_pruned_func;
|
||||
const IceFieldTrials* ice_field_trials;
|
||||
std::string ice_controller_field_trials;
|
||||
};
|
||||
|
||||
class IceControllerFactoryInterface {
|
||||
public:
|
||||
virtual ~IceControllerFactoryInterface() = default;
|
||||
virtual std::unique_ptr<IceControllerInterface> Create(
|
||||
const IceControllerFactoryArgs& args) = 0;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_ICE_CONTROLLER_FACTORY_INTERFACE_H_
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2019 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/ice_controller_interface.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "p2p/base/ice_switch_reason.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
std::string IceRecheckEvent::ToString() const {
|
||||
std::string str = IceSwitchReasonToString(reason);
|
||||
if (recheck_delay_ms) {
|
||||
str += " (after delay: " + std::to_string(recheck_delay_ms) + ")";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright 2019 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_ICE_CONTROLLER_INTERFACE_H_
|
||||
#define P2P_BASE_ICE_CONTROLLER_INTERFACE_H_
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "p2p/base/connection.h"
|
||||
#include "p2p/base/ice_switch_reason.h"
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
struct IceFieldTrials; // Forward declaration to avoid circular dependency.
|
||||
|
||||
struct RTC_EXPORT IceRecheckEvent {
|
||||
IceRecheckEvent(IceSwitchReason _reason, int _recheck_delay_ms)
|
||||
: reason(_reason), recheck_delay_ms(_recheck_delay_ms) {}
|
||||
|
||||
std::string ToString() const;
|
||||
|
||||
IceSwitchReason reason;
|
||||
int recheck_delay_ms;
|
||||
};
|
||||
|
||||
// Defines the interface for a module that control
|
||||
// - which connection to ping
|
||||
// - which connection to use
|
||||
// - which connection to prune
|
||||
// - which connection to forget learned state on
|
||||
//
|
||||
// The P2PTransportChannel owns (creates and destroys) Connections,
|
||||
// but P2PTransportChannel gives const pointers to the the IceController using
|
||||
// `AddConnection`, i.e the IceController should not call any non-const methods
|
||||
// on a Connection but signal back in the interface if any mutable function
|
||||
// shall be called.
|
||||
//
|
||||
// Current these are limited to:
|
||||
// Connection::Ping - returned in PingResult
|
||||
// Connection::Prune - retuned in PruneConnections
|
||||
// Connection::ForgetLearnedState - return in SwitchResult
|
||||
//
|
||||
// The IceController shall keep track of all connections added
|
||||
// (and not destroyed) and give them back using the GetConnections() function.
|
||||
//
|
||||
// When a Connection gets destroyed
|
||||
// - signals on Connection::SignalDestroyed
|
||||
// - P2PTransportChannel calls IceController::OnConnectionDestroyed
|
||||
class IceControllerInterface {
|
||||
public:
|
||||
// This represents the result of a switch call.
|
||||
struct SwitchResult {
|
||||
// Connection that we should (optionally) switch to.
|
||||
absl::optional<const Connection*> connection;
|
||||
|
||||
// An optional recheck event for when a Switch() should be attempted again.
|
||||
absl::optional<IceRecheckEvent> recheck_event;
|
||||
|
||||
// A vector with connection to run ForgetLearnedState on.
|
||||
std::vector<const Connection*> connections_to_forget_state_on;
|
||||
};
|
||||
|
||||
// This represents the result of a call to SelectConnectionToPing.
|
||||
struct PingResult {
|
||||
PingResult(const Connection* conn, int _recheck_delay_ms)
|
||||
: connection(conn ? absl::optional<const Connection*>(conn)
|
||||
: absl::nullopt),
|
||||
recheck_delay_ms(_recheck_delay_ms) {}
|
||||
|
||||
// Connection that we should (optionally) ping.
|
||||
const absl::optional<const Connection*> connection;
|
||||
|
||||
// The delay before P2PTransportChannel shall call SelectConnectionToPing()
|
||||
// again.
|
||||
//
|
||||
// Since the IceController determines which connection to ping and
|
||||
// only returns one connection at a time, the recheck_delay_ms does not have
|
||||
// any obvious implication on bitrate for pings. E.g the recheck_delay_ms
|
||||
// will be shorter if there are more connections available.
|
||||
const int recheck_delay_ms = 0;
|
||||
};
|
||||
|
||||
virtual ~IceControllerInterface() = default;
|
||||
|
||||
// These setters are called when the state of P2PTransportChannel is mutated.
|
||||
virtual void SetIceConfig(const IceConfig& config) = 0;
|
||||
virtual void SetSelectedConnection(const Connection* selected_connection) = 0;
|
||||
virtual void AddConnection(const Connection* connection) = 0;
|
||||
virtual void OnConnectionDestroyed(const Connection* connection) = 0;
|
||||
|
||||
// These are all connections that has been added and not destroyed.
|
||||
virtual rtc::ArrayView<const Connection* const> GetConnections() const {
|
||||
// Stub implementation to simplify downstream roll.
|
||||
RTC_CHECK_NOTREACHED();
|
||||
return {};
|
||||
}
|
||||
// TODO(bugs.webrtc.org/15702): Remove this after downstream is cleaned up.
|
||||
virtual rtc::ArrayView<const Connection*> connections() const {
|
||||
// Stub implementation to simplify downstream removal.
|
||||
RTC_CHECK_NOTREACHED();
|
||||
return {};
|
||||
}
|
||||
|
||||
// Is there a pingable connection ?
|
||||
// This function is used to boot-strap pinging, after this returns true
|
||||
// SelectConnectionToPing() will be called periodically.
|
||||
virtual bool HasPingableConnection() const = 0;
|
||||
|
||||
// Select a connection to Ping, or nullptr if none.
|
||||
virtual PingResult SelectConnectionToPing(int64_t last_ping_sent_ms) = 0;
|
||||
|
||||
// Compute the "STUN_ATTR_USE_CANDIDATE" for `conn`.
|
||||
virtual bool GetUseCandidateAttr(const Connection* conn,
|
||||
NominationMode mode,
|
||||
IceMode remote_ice_mode) const = 0;
|
||||
|
||||
// These methods is only added to not have to change all unit tests
|
||||
// that simulate pinging by marking a connection pinged.
|
||||
virtual const Connection* FindNextPingableConnection() = 0;
|
||||
virtual void MarkConnectionPinged(const Connection* con) = 0;
|
||||
|
||||
// Check if we should switch to `connection`.
|
||||
// This method is called for IceSwitchReasons that can switch directly
|
||||
// i.e without resorting.
|
||||
virtual SwitchResult ShouldSwitchConnection(IceSwitchReason reason,
|
||||
const Connection* connection) = 0;
|
||||
|
||||
// Sort connections and check if we should switch.
|
||||
virtual SwitchResult SortAndSwitchConnection(IceSwitchReason reason) = 0;
|
||||
|
||||
// Prune connections.
|
||||
virtual std::vector<const Connection*> PruneConnections() = 0;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_ICE_CONTROLLER_INTERFACE_H_
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/ice_credentials_iterator.h"
|
||||
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "rtc_base/helpers.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
IceCredentialsIterator::IceCredentialsIterator(
|
||||
const std::vector<IceParameters>& pooled_credentials)
|
||||
: pooled_ice_credentials_(pooled_credentials) {}
|
||||
|
||||
IceCredentialsIterator::~IceCredentialsIterator() = default;
|
||||
|
||||
IceParameters IceCredentialsIterator::CreateRandomIceCredentials() {
|
||||
return IceParameters(rtc::CreateRandomString(ICE_UFRAG_LENGTH),
|
||||
rtc::CreateRandomString(ICE_PWD_LENGTH), false);
|
||||
}
|
||||
|
||||
IceParameters IceCredentialsIterator::GetIceCredentials() {
|
||||
if (pooled_ice_credentials_.empty()) {
|
||||
return CreateRandomIceCredentials();
|
||||
}
|
||||
IceParameters credentials = pooled_ice_credentials_.back();
|
||||
pooled_ice_credentials_.pop_back();
|
||||
return credentials;
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_ICE_CREDENTIALS_ITERATOR_H_
|
||||
#define P2P_BASE_ICE_CREDENTIALS_ITERATOR_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "p2p/base/transport_description.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class IceCredentialsIterator {
|
||||
public:
|
||||
explicit IceCredentialsIterator(const std::vector<IceParameters>&);
|
||||
virtual ~IceCredentialsIterator();
|
||||
|
||||
// Get next pooled ice credentials.
|
||||
// Returns a new random credential if the pool is empty.
|
||||
IceParameters GetIceCredentials();
|
||||
|
||||
static IceParameters CreateRandomIceCredentials();
|
||||
|
||||
private:
|
||||
std::vector<IceParameters> pooled_ice_credentials_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_ICE_CREDENTIALS_ITERATOR_H_
|
||||
47
TMessagesProj/jni/voip/webrtc/p2p/base/ice_switch_reason.cc
Normal file
47
TMessagesProj/jni/voip/webrtc/p2p/base/ice_switch_reason.cc
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2022 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/ice_switch_reason.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace cricket {
|
||||
|
||||
std::string IceSwitchReasonToString(IceSwitchReason reason) {
|
||||
switch (reason) {
|
||||
case IceSwitchReason::REMOTE_CANDIDATE_GENERATION_CHANGE:
|
||||
return "remote candidate generation maybe changed";
|
||||
case IceSwitchReason::NETWORK_PREFERENCE_CHANGE:
|
||||
return "network preference changed";
|
||||
case IceSwitchReason::NEW_CONNECTION_FROM_LOCAL_CANDIDATE:
|
||||
return "new candidate pairs created from a new local candidate";
|
||||
case IceSwitchReason::NEW_CONNECTION_FROM_REMOTE_CANDIDATE:
|
||||
return "new candidate pairs created from a new remote candidate";
|
||||
case IceSwitchReason::NEW_CONNECTION_FROM_UNKNOWN_REMOTE_ADDRESS:
|
||||
return "a new candidate pair created from an unknown remote address";
|
||||
case IceSwitchReason::NOMINATION_ON_CONTROLLED_SIDE:
|
||||
return "nomination on the controlled side";
|
||||
case IceSwitchReason::DATA_RECEIVED:
|
||||
return "data received";
|
||||
case IceSwitchReason::CONNECT_STATE_CHANGE:
|
||||
return "candidate pair state changed";
|
||||
case IceSwitchReason::SELECTED_CONNECTION_DESTROYED:
|
||||
return "selected candidate pair destroyed";
|
||||
case IceSwitchReason::ICE_CONTROLLER_RECHECK:
|
||||
return "ice-controller-request-recheck";
|
||||
case IceSwitchReason::APPLICATION_REQUESTED:
|
||||
return "application requested";
|
||||
case IceSwitchReason::UNKNOWN:
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
43
TMessagesProj/jni/voip/webrtc/p2p/base/ice_switch_reason.h
Normal file
43
TMessagesProj/jni/voip/webrtc/p2p/base/ice_switch_reason.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2022 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_ICE_SWITCH_REASON_H_
|
||||
#define P2P_BASE_ICE_SWITCH_REASON_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
enum class IceSwitchReason {
|
||||
UNKNOWN,
|
||||
REMOTE_CANDIDATE_GENERATION_CHANGE,
|
||||
NETWORK_PREFERENCE_CHANGE,
|
||||
NEW_CONNECTION_FROM_LOCAL_CANDIDATE,
|
||||
NEW_CONNECTION_FROM_REMOTE_CANDIDATE,
|
||||
NEW_CONNECTION_FROM_UNKNOWN_REMOTE_ADDRESS,
|
||||
NOMINATION_ON_CONTROLLED_SIDE,
|
||||
DATA_RECEIVED,
|
||||
CONNECT_STATE_CHANGE,
|
||||
SELECTED_CONNECTION_DESTROYED,
|
||||
// The ICE_CONTROLLER_RECHECK enum value lets an IceController request
|
||||
// P2PTransportChannel to recheck a switch periodically without an event
|
||||
// taking place.
|
||||
ICE_CONTROLLER_RECHECK,
|
||||
// The webrtc application requested a connection switch.
|
||||
APPLICATION_REQUESTED,
|
||||
};
|
||||
|
||||
RTC_EXPORT std::string IceSwitchReasonToString(IceSwitchReason reason);
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_ICE_SWITCH_REASON_H_
|
||||
146
TMessagesProj/jni/voip/webrtc/p2p/base/ice_transport_internal.cc
Normal file
146
TMessagesProj/jni/voip/webrtc/p2p/base/ice_transport_internal.cc
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright 2017 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
using webrtc::RTCError;
|
||||
using webrtc::RTCErrorType;
|
||||
|
||||
RTCError VerifyCandidate(const Candidate& cand) {
|
||||
// No address zero.
|
||||
if (cand.address().IsNil() || cand.address().IsAnyIP()) {
|
||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
||||
"candidate has address of zero");
|
||||
}
|
||||
|
||||
// Disallow all ports below 1024, except for 80 and 443 on public addresses.
|
||||
int port = cand.address().port();
|
||||
if (cand.protocol() == cricket::TCP_PROTOCOL_NAME &&
|
||||
(cand.tcptype() == cricket::TCPTYPE_ACTIVE_STR || port == 0)) {
|
||||
// Expected for active-only candidates per
|
||||
// http://tools.ietf.org/html/rfc6544#section-4.5 so no error.
|
||||
// Libjingle clients emit port 0, in "active" mode.
|
||||
return RTCError::OK();
|
||||
}
|
||||
if (port < 1024) {
|
||||
if ((port != 80) && (port != 443)) {
|
||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
||||
"candidate has port below 1024, but not 80 or 443");
|
||||
}
|
||||
|
||||
if (cand.address().IsPrivateIP()) {
|
||||
return RTCError(
|
||||
RTCErrorType::INVALID_PARAMETER,
|
||||
"candidate has port of 80 or 443 with private IP address");
|
||||
}
|
||||
}
|
||||
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
RTCError VerifyCandidates(const Candidates& candidates) {
|
||||
for (const Candidate& candidate : candidates) {
|
||||
RTCError error = VerifyCandidate(candidate);
|
||||
if (!error.ok())
|
||||
return error;
|
||||
}
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
IceConfig::IceConfig() = default;
|
||||
|
||||
IceConfig::IceConfig(int receiving_timeout_ms,
|
||||
int backup_connection_ping_interval,
|
||||
ContinualGatheringPolicy gathering_policy,
|
||||
bool prioritize_most_likely_candidate_pairs,
|
||||
int stable_writable_connection_ping_interval_ms,
|
||||
bool presume_writable_when_fully_relayed,
|
||||
int regather_on_failed_networks_interval_ms,
|
||||
int receiving_switching_delay_ms)
|
||||
: receiving_timeout(receiving_timeout_ms),
|
||||
backup_connection_ping_interval(backup_connection_ping_interval),
|
||||
continual_gathering_policy(gathering_policy),
|
||||
prioritize_most_likely_candidate_pairs(
|
||||
prioritize_most_likely_candidate_pairs),
|
||||
stable_writable_connection_ping_interval(
|
||||
stable_writable_connection_ping_interval_ms),
|
||||
presume_writable_when_fully_relayed(presume_writable_when_fully_relayed),
|
||||
regather_on_failed_networks_interval(
|
||||
regather_on_failed_networks_interval_ms),
|
||||
receiving_switching_delay(receiving_switching_delay_ms) {}
|
||||
|
||||
IceConfig::~IceConfig() = default;
|
||||
|
||||
int IceConfig::receiving_timeout_or_default() const {
|
||||
return receiving_timeout.value_or(RECEIVING_TIMEOUT);
|
||||
}
|
||||
int IceConfig::backup_connection_ping_interval_or_default() const {
|
||||
return backup_connection_ping_interval.value_or(
|
||||
BACKUP_CONNECTION_PING_INTERVAL);
|
||||
}
|
||||
int IceConfig::stable_writable_connection_ping_interval_or_default() const {
|
||||
return stable_writable_connection_ping_interval.value_or(
|
||||
STRONG_AND_STABLE_WRITABLE_CONNECTION_PING_INTERVAL);
|
||||
}
|
||||
int IceConfig::regather_on_failed_networks_interval_or_default() const {
|
||||
return regather_on_failed_networks_interval.value_or(
|
||||
REGATHER_ON_FAILED_NETWORKS_INTERVAL);
|
||||
}
|
||||
int IceConfig::receiving_switching_delay_or_default() const {
|
||||
return receiving_switching_delay.value_or(RECEIVING_SWITCHING_DELAY);
|
||||
}
|
||||
int IceConfig::ice_check_interval_strong_connectivity_or_default() const {
|
||||
return ice_check_interval_strong_connectivity.value_or(STRONG_PING_INTERVAL);
|
||||
}
|
||||
int IceConfig::ice_check_interval_weak_connectivity_or_default() const {
|
||||
return ice_check_interval_weak_connectivity.value_or(WEAK_PING_INTERVAL);
|
||||
}
|
||||
int IceConfig::ice_check_min_interval_or_default() const {
|
||||
return ice_check_min_interval.value_or(-1);
|
||||
}
|
||||
int IceConfig::ice_unwritable_timeout_or_default() const {
|
||||
return ice_unwritable_timeout.value_or(CONNECTION_WRITE_CONNECT_TIMEOUT);
|
||||
}
|
||||
int IceConfig::ice_unwritable_min_checks_or_default() const {
|
||||
return ice_unwritable_min_checks.value_or(CONNECTION_WRITE_CONNECT_FAILURES);
|
||||
}
|
||||
int IceConfig::ice_inactive_timeout_or_default() const {
|
||||
return ice_inactive_timeout.value_or(CONNECTION_WRITE_TIMEOUT);
|
||||
}
|
||||
int IceConfig::stun_keepalive_interval_or_default() const {
|
||||
return stun_keepalive_interval.value_or(STUN_KEEPALIVE_INTERVAL);
|
||||
}
|
||||
|
||||
IceTransportInternal::IceTransportInternal() {
|
||||
// Set up detector for use of SignalGatheringState rather than
|
||||
// SetGatheringStateCallback, and behave accordingly
|
||||
// TODO(bugs.webrtc.org/11943): remove when Signal removed
|
||||
SignalGatheringState.connect(
|
||||
this, &IceTransportInternal::SignalGatheringStateFired);
|
||||
}
|
||||
|
||||
IceTransportInternal::~IceTransportInternal() = default;
|
||||
|
||||
void IceTransportInternal::SetIceCredentials(absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd) {
|
||||
SetIceParameters(IceParameters(ice_ufrag, ice_pwd, false));
|
||||
}
|
||||
|
||||
void IceTransportInternal::SetRemoteIceCredentials(absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd) {
|
||||
SetRemoteIceParameters(IceParameters(ice_ufrag, ice_pwd, false));
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
418
TMessagesProj/jni/voip/webrtc/p2p/base/ice_transport_internal.h
Normal file
418
TMessagesProj/jni/voip/webrtc/p2p/base/ice_transport_internal.h
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_ICE_TRANSPORT_INTERNAL_H_
|
||||
#define P2P_BASE_ICE_TRANSPORT_INTERNAL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/candidate.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/transport/enums.h"
|
||||
#include "p2p/base/connection.h"
|
||||
#include "p2p/base/packet_transport_internal.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "p2p/base/stun_dictionary.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "rtc_base/network_constants.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
struct IceTransportStats {
|
||||
CandidateStatsList candidate_stats_list;
|
||||
ConnectionInfos connection_infos;
|
||||
// Number of times the selected candidate pair has changed
|
||||
// Initially 0 and 1 once the first candidate pair has been selected.
|
||||
// The counter is increase also when "unselecting" a connection.
|
||||
uint32_t selected_candidate_pair_changes = 0;
|
||||
|
||||
// Bytes/packets sent/received.
|
||||
// note: Is not the same as sum(connection_infos.bytes_sent)
|
||||
// as connections are created and destroyed while the ICE transport
|
||||
// is alive.
|
||||
uint64_t bytes_sent = 0;
|
||||
uint64_t bytes_received = 0;
|
||||
uint64_t packets_sent = 0;
|
||||
uint64_t packets_received = 0;
|
||||
|
||||
IceRole ice_role = ICEROLE_UNKNOWN;
|
||||
std::string ice_local_username_fragment;
|
||||
webrtc::IceTransportState ice_state = webrtc::IceTransportState::kNew;
|
||||
};
|
||||
|
||||
typedef std::vector<Candidate> Candidates;
|
||||
|
||||
enum IceConnectionState {
|
||||
kIceConnectionConnecting = 0,
|
||||
kIceConnectionFailed,
|
||||
kIceConnectionConnected, // Writable, but still checking one or more
|
||||
// connections
|
||||
kIceConnectionCompleted,
|
||||
};
|
||||
|
||||
// TODO(deadbeef): Unify with PeerConnectionInterface::IceConnectionState
|
||||
// once /talk/ and /webrtc/ are combined, and also switch to ENUM_NAME naming
|
||||
// style.
|
||||
enum IceGatheringState {
|
||||
kIceGatheringNew = 0,
|
||||
kIceGatheringGathering,
|
||||
kIceGatheringComplete,
|
||||
};
|
||||
|
||||
enum ContinualGatheringPolicy {
|
||||
// All port allocator sessions will stop after a writable connection is found.
|
||||
GATHER_ONCE = 0,
|
||||
// The most recent port allocator session will keep on running.
|
||||
GATHER_CONTINUALLY,
|
||||
};
|
||||
|
||||
// ICE Nomination mode.
|
||||
enum class NominationMode {
|
||||
REGULAR, // Nominate once per ICE restart (Not implemented yet).
|
||||
AGGRESSIVE, // Nominate every connection except that it will behave as if
|
||||
// REGULAR when the remote is an ICE-LITE endpoint.
|
||||
SEMI_AGGRESSIVE // Our current implementation of the nomination algorithm.
|
||||
// The details are described in P2PTransportChannel.
|
||||
};
|
||||
|
||||
// Utility method that checks if various required Candidate fields are filled in
|
||||
// and contain valid values. If conditions are not met, an RTCError with the
|
||||
// appropriated error number and description is returned. If the configuration
|
||||
// is valid RTCError::OK() is returned.
|
||||
webrtc::RTCError VerifyCandidate(const Candidate& cand);
|
||||
|
||||
// Runs through a list of cricket::Candidate instances and calls VerifyCandidate
|
||||
// for each one, stopping on the first error encounted and returning that error
|
||||
// value if so. On success returns RTCError::OK().
|
||||
webrtc::RTCError VerifyCandidates(const Candidates& candidates);
|
||||
|
||||
// Information about ICE configuration.
|
||||
// TODO(deadbeef): Use absl::optional to represent unset values, instead of
|
||||
// -1.
|
||||
//
|
||||
// TODO(bugs.webrtc.org/15609): Define a public API for this.
|
||||
struct RTC_EXPORT IceConfig {
|
||||
// The ICE connection receiving timeout value in milliseconds.
|
||||
absl::optional<int> receiving_timeout;
|
||||
// Time interval in milliseconds to ping a backup connection when the ICE
|
||||
// channel is strongly connected.
|
||||
absl::optional<int> backup_connection_ping_interval;
|
||||
|
||||
ContinualGatheringPolicy continual_gathering_policy = GATHER_ONCE;
|
||||
|
||||
bool gather_continually() const {
|
||||
return continual_gathering_policy == GATHER_CONTINUALLY;
|
||||
}
|
||||
|
||||
// Whether we should prioritize Relay/Relay candidate when nothing
|
||||
// is writable yet.
|
||||
bool prioritize_most_likely_candidate_pairs = false;
|
||||
|
||||
// Writable connections are pinged at a slower rate once stablized.
|
||||
absl::optional<int> stable_writable_connection_ping_interval;
|
||||
|
||||
// If set to true, this means the ICE transport should presume TURN-to-TURN
|
||||
// candidate pairs will succeed, even before a binding response is received.
|
||||
bool presume_writable_when_fully_relayed = false;
|
||||
|
||||
// If true, after the ICE transport type (as the candidate filter used by the
|
||||
// port allocator) is changed such that new types of ICE candidates are
|
||||
// allowed by the new filter, e.g. from CF_RELAY to CF_ALL, candidates that
|
||||
// have been gathered by the ICE transport but filtered out and not signaled
|
||||
// to the upper layers, will be surfaced.
|
||||
bool surface_ice_candidates_on_ice_transport_type_changed = false;
|
||||
|
||||
// Interval to check on all networks and to perform ICE regathering on any
|
||||
// active network having no connection on it.
|
||||
absl::optional<int> regather_on_failed_networks_interval;
|
||||
|
||||
// The time period in which we will not switch the selected connection
|
||||
// when a new connection becomes receiving but the selected connection is not
|
||||
// in case that the selected connection may become receiving soon.
|
||||
absl::optional<int> receiving_switching_delay;
|
||||
|
||||
// TODO(honghaiz): Change the default to regular nomination.
|
||||
// Default nomination mode if the remote does not support renomination.
|
||||
NominationMode default_nomination_mode = NominationMode::SEMI_AGGRESSIVE;
|
||||
|
||||
// The interval in milliseconds at which ICE checks (STUN pings) will be sent
|
||||
// for a candidate pair when it is both writable and receiving (strong
|
||||
// connectivity). This parameter overrides the default value given by
|
||||
// `STRONG_PING_INTERVAL` in p2ptransport.h if set.
|
||||
absl::optional<int> ice_check_interval_strong_connectivity;
|
||||
// The interval in milliseconds at which ICE checks (STUN pings) will be sent
|
||||
// for a candidate pair when it is either not writable or not receiving (weak
|
||||
// connectivity). This parameter overrides the default value given by
|
||||
// `WEAK_PING_INTERVAL` in p2ptransport.h if set.
|
||||
absl::optional<int> ice_check_interval_weak_connectivity;
|
||||
// ICE checks (STUN pings) will not be sent at higher rate (lower interval)
|
||||
// than this, no matter what other settings there are.
|
||||
// Measure in milliseconds.
|
||||
//
|
||||
// Note that this parameter overrides both the above check intervals for
|
||||
// candidate pairs with strong or weak connectivity, if either of the above
|
||||
// interval is shorter than the min interval.
|
||||
absl::optional<int> ice_check_min_interval;
|
||||
// The min time period for which a candidate pair must wait for response to
|
||||
// connectivity checks before it becomes unwritable. This parameter
|
||||
// overrides the default value given by `CONNECTION_WRITE_CONNECT_TIMEOUT`
|
||||
// in port.h if set, when determining the writability of a candidate pair.
|
||||
absl::optional<int> ice_unwritable_timeout;
|
||||
|
||||
// The min number of connectivity checks that a candidate pair must sent
|
||||
// without receiving response before it becomes unwritable. This parameter
|
||||
// overrides the default value given by `CONNECTION_WRITE_CONNECT_FAILURES` in
|
||||
// port.h if set, when determining the writability of a candidate pair.
|
||||
absl::optional<int> ice_unwritable_min_checks;
|
||||
|
||||
// The min time period for which a candidate pair must wait for response to
|
||||
// connectivity checks it becomes inactive. This parameter overrides the
|
||||
// default value given by `CONNECTION_WRITE_TIMEOUT` in port.h if set, when
|
||||
// determining the writability of a candidate pair.
|
||||
absl::optional<int> ice_inactive_timeout;
|
||||
|
||||
// The interval in milliseconds at which STUN candidates will resend STUN
|
||||
// binding requests to keep NAT bindings open.
|
||||
absl::optional<int> stun_keepalive_interval;
|
||||
|
||||
absl::optional<rtc::AdapterType> network_preference;
|
||||
|
||||
webrtc::VpnPreference vpn_preference = webrtc::VpnPreference::kDefault;
|
||||
|
||||
IceConfig();
|
||||
IceConfig(int receiving_timeout_ms,
|
||||
int backup_connection_ping_interval,
|
||||
ContinualGatheringPolicy gathering_policy,
|
||||
bool prioritize_most_likely_candidate_pairs,
|
||||
int stable_writable_connection_ping_interval_ms,
|
||||
bool presume_writable_when_fully_relayed,
|
||||
int regather_on_failed_networks_interval_ms,
|
||||
int receiving_switching_delay_ms);
|
||||
~IceConfig();
|
||||
|
||||
// Helper getters for parameters with implementation-specific default value.
|
||||
// By convention, parameters with default value are represented by
|
||||
// absl::optional and setting a parameter to null restores its default value.
|
||||
int receiving_timeout_or_default() const;
|
||||
int backup_connection_ping_interval_or_default() const;
|
||||
int stable_writable_connection_ping_interval_or_default() const;
|
||||
int regather_on_failed_networks_interval_or_default() const;
|
||||
int receiving_switching_delay_or_default() const;
|
||||
int ice_check_interval_strong_connectivity_or_default() const;
|
||||
int ice_check_interval_weak_connectivity_or_default() const;
|
||||
int ice_check_min_interval_or_default() const;
|
||||
int ice_unwritable_timeout_or_default() const;
|
||||
int ice_unwritable_min_checks_or_default() const;
|
||||
int ice_inactive_timeout_or_default() const;
|
||||
int stun_keepalive_interval_or_default() const;
|
||||
};
|
||||
|
||||
// TODO(zhihuang): Replace this with
|
||||
// PeerConnectionInterface::IceConnectionState.
|
||||
enum class IceTransportState {
|
||||
STATE_INIT,
|
||||
STATE_CONNECTING, // Will enter this state once a connection is created
|
||||
STATE_COMPLETED,
|
||||
STATE_FAILED
|
||||
};
|
||||
|
||||
// IceTransportInternal is an internal abstract class that does ICE.
|
||||
// Once the public interface is supported,
|
||||
// (https://www.w3.org/TR/webrtc/#rtcicetransport)
|
||||
// the IceTransportInterface will be split from this class.
|
||||
//
|
||||
// TODO(bugs.webrtc.org/15609): Define a public API for this.
|
||||
class RTC_EXPORT IceTransportInternal : public rtc::PacketTransportInternal {
|
||||
public:
|
||||
IceTransportInternal();
|
||||
~IceTransportInternal() override;
|
||||
|
||||
// TODO(bugs.webrtc.org/9308): Remove GetState once all uses have been
|
||||
// migrated to GetIceTransportState.
|
||||
virtual IceTransportState GetState() const = 0;
|
||||
virtual webrtc::IceTransportState GetIceTransportState() const = 0;
|
||||
|
||||
virtual int component() const = 0;
|
||||
|
||||
virtual IceRole GetIceRole() const = 0;
|
||||
|
||||
virtual void SetIceRole(IceRole role) = 0;
|
||||
|
||||
virtual void SetIceTiebreaker(uint64_t tiebreaker) = 0;
|
||||
|
||||
virtual void SetIceCredentials(absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd);
|
||||
|
||||
virtual void SetRemoteIceCredentials(absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd);
|
||||
|
||||
// The ufrag and pwd in `ice_params` must be set
|
||||
// before candidate gathering can start.
|
||||
virtual void SetIceParameters(const IceParameters& ice_params) = 0;
|
||||
|
||||
virtual void SetRemoteIceParameters(const IceParameters& ice_params) = 0;
|
||||
|
||||
virtual void SetRemoteIceMode(IceMode mode) = 0;
|
||||
|
||||
virtual void SetIceConfig(const IceConfig& config) = 0;
|
||||
|
||||
// Start gathering candidates if not already started, or if an ICE restart
|
||||
// occurred.
|
||||
virtual void MaybeStartGathering() = 0;
|
||||
|
||||
virtual void AddRemoteCandidate(const Candidate& candidate) = 0;
|
||||
|
||||
virtual void RemoveRemoteCandidate(const Candidate& candidate) = 0;
|
||||
|
||||
virtual void RemoveAllRemoteCandidates() = 0;
|
||||
|
||||
virtual IceGatheringState gathering_state() const = 0;
|
||||
|
||||
// Returns the current stats for this connection.
|
||||
virtual bool GetStats(IceTransportStats* ice_transport_stats) = 0;
|
||||
|
||||
// Returns RTT estimate over the currently active connection, or an empty
|
||||
// absl::optional if there is none.
|
||||
virtual absl::optional<int> GetRttEstimate() = 0;
|
||||
|
||||
// TODO(qingsi): Remove this method once Chrome does not depend on it anymore.
|
||||
virtual const Connection* selected_connection() const = 0;
|
||||
|
||||
// Returns the selected candidate pair, or an empty absl::optional if there is
|
||||
// none.
|
||||
virtual absl::optional<const CandidatePair> GetSelectedCandidatePair()
|
||||
const = 0;
|
||||
|
||||
virtual absl::optional<std::reference_wrapper<StunDictionaryWriter>>
|
||||
GetDictionaryWriter() {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
// Signal Exposed for backwards compatibility.
|
||||
sigslot::signal1<IceTransportInternal*> SignalGatheringState;
|
||||
void SetGatheringStateCallback(
|
||||
absl::AnyInvocable<void(IceTransportInternal*)> callback) {
|
||||
RTC_DCHECK(!gathering_state_callback_);
|
||||
gathering_state_callback_ = std::move(callback);
|
||||
}
|
||||
|
||||
// Handles sending and receiving of candidates.
|
||||
sigslot::signal2<IceTransportInternal*, const Candidate&>
|
||||
SignalCandidateGathered;
|
||||
|
||||
void SetCandidateErrorCallback(
|
||||
absl::AnyInvocable<void(IceTransportInternal*,
|
||||
const IceCandidateErrorEvent&)> callback) {
|
||||
RTC_DCHECK(!candidate_error_callback_);
|
||||
candidate_error_callback_ = std::move(callback);
|
||||
}
|
||||
|
||||
void SetCandidatesRemovedCallback(
|
||||
absl::AnyInvocable<void(IceTransportInternal*, const Candidates&)>
|
||||
callback) {
|
||||
RTC_DCHECK(!candidates_removed_callback_);
|
||||
candidates_removed_callback_ = std::move(callback);
|
||||
}
|
||||
|
||||
// Deprecated by PacketTransportInternal::SignalNetworkRouteChanged.
|
||||
// This signal occurs when there is a change in the way that packets are
|
||||
// being routed, i.e. to a different remote location. The candidate
|
||||
// indicates where and how we are currently sending media.
|
||||
// TODO(zhihuang): Update the Chrome remoting to use the new
|
||||
// SignalNetworkRouteChanged.
|
||||
sigslot::signal2<IceTransportInternal*, const Candidate&> SignalRouteChange;
|
||||
|
||||
void SetCandidatePairChangeCallback(
|
||||
absl::AnyInvocable<void(const cricket::CandidatePairChangeEvent&)>
|
||||
callback) {
|
||||
RTC_DCHECK(!candidate_pair_change_callback_);
|
||||
candidate_pair_change_callback_ = std::move(callback);
|
||||
}
|
||||
|
||||
// Invoked when there is conflict in the ICE role between local and remote
|
||||
// agents.
|
||||
sigslot::signal1<IceTransportInternal*> SignalRoleConflict;
|
||||
|
||||
// Emitted whenever the transport state changed.
|
||||
// TODO(bugs.webrtc.org/9308): Remove once all uses have migrated to the new
|
||||
// IceTransportState.
|
||||
sigslot::signal1<IceTransportInternal*> SignalStateChanged;
|
||||
|
||||
// Emitted whenever the new standards-compliant transport state changed.
|
||||
sigslot::signal1<IceTransportInternal*> SignalIceTransportStateChanged;
|
||||
|
||||
// Invoked when the transport is being destroyed.
|
||||
sigslot::signal1<IceTransportInternal*> SignalDestroyed;
|
||||
|
||||
// Invoked when remote dictionary has been updated,
|
||||
// i.e. modifications to attributes from remote ice agent has
|
||||
// reflected in our StunDictionaryView.
|
||||
template <typename F>
|
||||
void AddDictionaryViewUpdatedCallback(const void* tag, F&& callback) {
|
||||
dictionary_view_updated_callback_list_.AddReceiver(
|
||||
tag, std::forward<F>(callback));
|
||||
}
|
||||
void RemoveDictionaryViewUpdatedCallback(const void* tag) {
|
||||
dictionary_view_updated_callback_list_.RemoveReceivers(tag);
|
||||
}
|
||||
|
||||
// Invoked when local dictionary has been synchronized,
|
||||
// i.e. remote ice agent has reported acknowledged updates from us.
|
||||
template <typename F>
|
||||
void AddDictionaryWriterSyncedCallback(const void* tag, F&& callback) {
|
||||
dictionary_writer_synced_callback_list_.AddReceiver(
|
||||
tag, std::forward<F>(callback));
|
||||
}
|
||||
void RemoveDictionaryWriterSyncedCallback(const void* tag) {
|
||||
dictionary_writer_synced_callback_list_.RemoveReceivers(tag);
|
||||
}
|
||||
|
||||
protected:
|
||||
void SendGatheringStateEvent() { SignalGatheringState(this); }
|
||||
|
||||
webrtc::CallbackList<IceTransportInternal*,
|
||||
const StunDictionaryView&,
|
||||
rtc::ArrayView<uint16_t>>
|
||||
dictionary_view_updated_callback_list_;
|
||||
webrtc::CallbackList<IceTransportInternal*, const StunDictionaryWriter&>
|
||||
dictionary_writer_synced_callback_list_;
|
||||
|
||||
absl::AnyInvocable<void(IceTransportInternal*)> gathering_state_callback_;
|
||||
|
||||
absl::AnyInvocable<void(IceTransportInternal*, const IceCandidateErrorEvent&)>
|
||||
candidate_error_callback_;
|
||||
|
||||
absl::AnyInvocable<void(IceTransportInternal*, const Candidates&)>
|
||||
candidates_removed_callback_;
|
||||
|
||||
absl::AnyInvocable<void(const cricket::CandidatePairChangeEvent&)>
|
||||
candidate_pair_change_callback_;
|
||||
|
||||
private:
|
||||
// TODO(bugs.webrtc.org/11943): remove when removing Signal
|
||||
void SignalGatheringStateFired(IceTransportInternal* transport) {
|
||||
if (gathering_state_callback_) {
|
||||
gathering_state_callback_(transport);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_ICE_TRANSPORT_INTERNAL_H_
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_MOCK_ACTIVE_ICE_CONTROLLER_H_
|
||||
#define P2P_BASE_MOCK_ACTIVE_ICE_CONTROLLER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "p2p/base/active_ice_controller_factory_interface.h"
|
||||
#include "p2p/base/active_ice_controller_interface.h"
|
||||
#include "test/gmock.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class MockActiveIceController : public cricket::ActiveIceControllerInterface {
|
||||
public:
|
||||
explicit MockActiveIceController(
|
||||
const cricket::ActiveIceControllerFactoryArgs& args) {}
|
||||
~MockActiveIceController() override = default;
|
||||
|
||||
MOCK_METHOD(void, SetIceConfig, (const cricket::IceConfig&), (override));
|
||||
MOCK_METHOD(void,
|
||||
OnConnectionAdded,
|
||||
(const cricket::Connection*),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
OnConnectionSwitched,
|
||||
(const cricket::Connection*),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
OnConnectionDestroyed,
|
||||
(const cricket::Connection*),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
OnConnectionPinged,
|
||||
(const cricket::Connection*),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
OnConnectionUpdated,
|
||||
(const cricket::Connection*),
|
||||
(override));
|
||||
MOCK_METHOD(bool,
|
||||
GetUseCandidateAttribute,
|
||||
(const cricket::Connection*,
|
||||
cricket::NominationMode,
|
||||
cricket::IceMode),
|
||||
(const, override));
|
||||
MOCK_METHOD(void,
|
||||
OnSortAndSwitchRequest,
|
||||
(cricket::IceSwitchReason),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
OnImmediateSortAndSwitchRequest,
|
||||
(cricket::IceSwitchReason),
|
||||
(override));
|
||||
MOCK_METHOD(bool,
|
||||
OnImmediateSwitchRequest,
|
||||
(cricket::IceSwitchReason, const cricket::Connection*),
|
||||
(override));
|
||||
MOCK_METHOD(const cricket::Connection*,
|
||||
FindNextPingableConnection,
|
||||
(),
|
||||
(override));
|
||||
};
|
||||
|
||||
class MockActiveIceControllerFactory
|
||||
: public cricket::ActiveIceControllerFactoryInterface {
|
||||
public:
|
||||
~MockActiveIceControllerFactory() override = default;
|
||||
|
||||
std::unique_ptr<cricket::ActiveIceControllerInterface> Create(
|
||||
const cricket::ActiveIceControllerFactoryArgs& args) {
|
||||
RecordActiveIceControllerCreated();
|
||||
return std::make_unique<MockActiveIceController>(args);
|
||||
}
|
||||
|
||||
MOCK_METHOD(void, RecordActiveIceControllerCreated, ());
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_MOCK_ACTIVE_ICE_CONTROLLER_H_
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_MOCK_DNS_RESOLVING_PACKET_SOCKET_FACTORY_H_
|
||||
#define P2P_BASE_MOCK_DNS_RESOLVING_PACKET_SOCKET_FACTORY_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "api/test/mock_async_dns_resolver.h"
|
||||
#include "p2p/base/basic_packet_socket_factory.h"
|
||||
|
||||
namespace rtc {
|
||||
|
||||
// A PacketSocketFactory implementation for tests that uses a mock DnsResolver
|
||||
// and allows setting expectations on the resolver and results.
|
||||
class MockDnsResolvingPacketSocketFactory : public BasicPacketSocketFactory {
|
||||
public:
|
||||
using Expectations = std::function<void(webrtc::MockAsyncDnsResolver*,
|
||||
webrtc::MockAsyncDnsResolverResult*)>;
|
||||
|
||||
explicit MockDnsResolvingPacketSocketFactory(SocketFactory* socket_factory)
|
||||
: BasicPacketSocketFactory(socket_factory) {}
|
||||
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface> CreateAsyncDnsResolver()
|
||||
override {
|
||||
std::unique_ptr<webrtc::MockAsyncDnsResolver> resolver =
|
||||
std::make_unique<webrtc::MockAsyncDnsResolver>();
|
||||
if (expectations_) {
|
||||
expectations_(resolver.get(), &resolver_result_);
|
||||
}
|
||||
return resolver;
|
||||
}
|
||||
|
||||
void SetExpectations(Expectations expectations) {
|
||||
expectations_ = expectations;
|
||||
}
|
||||
|
||||
private:
|
||||
webrtc::MockAsyncDnsResolverResult resolver_result_;
|
||||
Expectations expectations_;
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif // P2P_BASE_MOCK_DNS_RESOLVING_PACKET_SOCKET_FACTORY_H_
|
||||
50
TMessagesProj/jni/voip/webrtc/p2p/base/mock_ice_agent.h
Normal file
50
TMessagesProj/jni/voip/webrtc/p2p/base/mock_ice_agent.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_MOCK_ICE_AGENT_H_
|
||||
#define P2P_BASE_MOCK_ICE_AGENT_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "p2p/base/connection.h"
|
||||
#include "p2p/base/ice_agent_interface.h"
|
||||
#include "p2p/base/ice_switch_reason.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "test/gmock.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class MockIceAgent : public IceAgentInterface {
|
||||
public:
|
||||
~MockIceAgent() override = default;
|
||||
|
||||
MOCK_METHOD(int64_t, GetLastPingSentMs, (), (override, const));
|
||||
MOCK_METHOD(IceRole, GetIceRole, (), (override, const));
|
||||
MOCK_METHOD(void, OnStartedPinging, (), (override));
|
||||
MOCK_METHOD(void, UpdateConnectionStates, (), (override));
|
||||
MOCK_METHOD(void, UpdateState, (), (override));
|
||||
MOCK_METHOD(void,
|
||||
ForgetLearnedStateForConnections,
|
||||
(rtc::ArrayView<const Connection* const>),
|
||||
(override));
|
||||
MOCK_METHOD(void, SendPingRequest, (const Connection*), (override));
|
||||
MOCK_METHOD(void,
|
||||
SwitchSelectedConnection,
|
||||
(const Connection*, IceSwitchReason),
|
||||
(override));
|
||||
MOCK_METHOD(bool,
|
||||
PruneConnections,
|
||||
(rtc::ArrayView<const Connection* const>),
|
||||
(override));
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_MOCK_ICE_AGENT_H_
|
||||
94
TMessagesProj/jni/voip/webrtc/p2p/base/mock_ice_controller.h
Normal file
94
TMessagesProj/jni/voip/webrtc/p2p/base/mock_ice_controller.h
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_MOCK_ICE_CONTROLLER_H_
|
||||
#define P2P_BASE_MOCK_ICE_CONTROLLER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "p2p/base/ice_controller_factory_interface.h"
|
||||
#include "p2p/base/ice_controller_interface.h"
|
||||
#include "test/gmock.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class MockIceController : public cricket::IceControllerInterface {
|
||||
public:
|
||||
explicit MockIceController(const cricket::IceControllerFactoryArgs& args) {}
|
||||
~MockIceController() override = default;
|
||||
|
||||
MOCK_METHOD(void, SetIceConfig, (const cricket::IceConfig&), (override));
|
||||
MOCK_METHOD(void,
|
||||
SetSelectedConnection,
|
||||
(const cricket::Connection*),
|
||||
(override));
|
||||
MOCK_METHOD(void, AddConnection, (const cricket::Connection*), (override));
|
||||
MOCK_METHOD(void,
|
||||
OnConnectionDestroyed,
|
||||
(const cricket::Connection*),
|
||||
(override));
|
||||
MOCK_METHOD(rtc::ArrayView<const cricket::Connection* const>,
|
||||
GetConnections,
|
||||
(),
|
||||
(const, override));
|
||||
MOCK_METHOD(rtc::ArrayView<const cricket::Connection*>,
|
||||
connections,
|
||||
(),
|
||||
(const, override));
|
||||
MOCK_METHOD(bool, HasPingableConnection, (), (const, override));
|
||||
MOCK_METHOD(cricket::IceControllerInterface::PingResult,
|
||||
SelectConnectionToPing,
|
||||
(int64_t),
|
||||
(override));
|
||||
MOCK_METHOD(bool,
|
||||
GetUseCandidateAttr,
|
||||
(const cricket::Connection*,
|
||||
cricket::NominationMode,
|
||||
cricket::IceMode),
|
||||
(const, override));
|
||||
MOCK_METHOD(const cricket::Connection*,
|
||||
FindNextPingableConnection,
|
||||
(),
|
||||
(override));
|
||||
MOCK_METHOD(void,
|
||||
MarkConnectionPinged,
|
||||
(const cricket::Connection*),
|
||||
(override));
|
||||
MOCK_METHOD(cricket::IceControllerInterface::SwitchResult,
|
||||
ShouldSwitchConnection,
|
||||
(cricket::IceSwitchReason, const cricket::Connection*),
|
||||
(override));
|
||||
MOCK_METHOD(cricket::IceControllerInterface::SwitchResult,
|
||||
SortAndSwitchConnection,
|
||||
(cricket::IceSwitchReason),
|
||||
(override));
|
||||
MOCK_METHOD(std::vector<const cricket::Connection*>,
|
||||
PruneConnections,
|
||||
(),
|
||||
(override));
|
||||
};
|
||||
|
||||
class MockIceControllerFactory : public cricket::IceControllerFactoryInterface {
|
||||
public:
|
||||
~MockIceControllerFactory() override = default;
|
||||
|
||||
std::unique_ptr<cricket::IceControllerInterface> Create(
|
||||
const cricket::IceControllerFactoryArgs& args) override {
|
||||
RecordIceControllerCreated();
|
||||
return std::make_unique<MockIceController>(args);
|
||||
}
|
||||
|
||||
MOCK_METHOD(void, RecordIceControllerCreated, ());
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_MOCK_ICE_CONTROLLER_H_
|
||||
90
TMessagesProj/jni/voip/webrtc/p2p/base/mock_ice_transport.h
Normal file
90
TMessagesProj/jni/voip/webrtc/p2p/base/mock_ice_transport.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_MOCK_ICE_TRANSPORT_H_
|
||||
#define P2P_BASE_MOCK_ICE_TRANSPORT_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "test/gmock.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// Used in Chromium/remoting/protocol/channel_socket_adapter_unittest.cc
|
||||
class MockIceTransport : public IceTransportInternal {
|
||||
public:
|
||||
MockIceTransport() {
|
||||
SignalReadyToSend(this);
|
||||
SignalWritableState(this);
|
||||
}
|
||||
|
||||
MOCK_METHOD(int,
|
||||
SendPacket,
|
||||
(const char* data,
|
||||
size_t len,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags),
|
||||
(override));
|
||||
MOCK_METHOD(int, SetOption, (rtc::Socket::Option opt, int value), (override));
|
||||
MOCK_METHOD(int, GetError, (), (override));
|
||||
MOCK_METHOD(cricket::IceRole, GetIceRole, (), (const, override));
|
||||
MOCK_METHOD(bool,
|
||||
GetStats,
|
||||
(cricket::IceTransportStats * ice_transport_stats),
|
||||
(override));
|
||||
|
||||
IceTransportState GetState() const override {
|
||||
return IceTransportState::STATE_INIT;
|
||||
}
|
||||
webrtc::IceTransportState GetIceTransportState() const override {
|
||||
return webrtc::IceTransportState::kNew;
|
||||
}
|
||||
|
||||
const std::string& transport_name() const override { return transport_name_; }
|
||||
int component() const override { return 0; }
|
||||
void SetIceRole(IceRole role) override {}
|
||||
void SetIceTiebreaker(uint64_t tiebreaker) override {}
|
||||
// The ufrag and pwd in `ice_params` must be set
|
||||
// before candidate gathering can start.
|
||||
void SetIceParameters(const IceParameters& ice_params) override {}
|
||||
void SetRemoteIceParameters(const IceParameters& ice_params) override {}
|
||||
void SetRemoteIceMode(IceMode mode) override {}
|
||||
void SetIceConfig(const IceConfig& config) override {}
|
||||
absl::optional<int> GetRttEstimate() override { return absl::nullopt; }
|
||||
const Connection* selected_connection() const override { return nullptr; }
|
||||
absl::optional<const CandidatePair> GetSelectedCandidatePair()
|
||||
const override {
|
||||
return absl::nullopt;
|
||||
}
|
||||
void MaybeStartGathering() override {}
|
||||
void AddRemoteCandidate(const Candidate& candidate) override {}
|
||||
void RemoveRemoteCandidate(const Candidate& candidate) override {}
|
||||
void RemoveAllRemoteCandidates() override {}
|
||||
IceGatheringState gathering_state() const override {
|
||||
return IceGatheringState::kIceGatheringComplete;
|
||||
}
|
||||
|
||||
bool receiving() const override { return true; }
|
||||
bool writable() const override { return true; }
|
||||
|
||||
private:
|
||||
std::string transport_name_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_MOCK_ICE_TRANSPORT_H_
|
||||
75
TMessagesProj/jni/voip/webrtc/p2p/base/p2p_constants.cc
Normal file
75
TMessagesProj/jni/voip/webrtc/p2p/base/p2p_constants.cc
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
const char CN_AUDIO[] = "audio";
|
||||
const char CN_VIDEO[] = "video";
|
||||
const char CN_DATA[] = "data";
|
||||
const char CN_OTHER[] = "main";
|
||||
|
||||
const char GROUP_TYPE_BUNDLE[] = "BUNDLE";
|
||||
|
||||
// Minimum ufrag length is 4 characters as per RFC5245.
|
||||
const int ICE_UFRAG_LENGTH = 4;
|
||||
// Minimum password length of 22 characters as per RFC5245. We chose 24 because
|
||||
// some internal systems expect password to be multiple of 4.
|
||||
const int ICE_PWD_LENGTH = 24;
|
||||
const size_t ICE_UFRAG_MIN_LENGTH = 4;
|
||||
const size_t ICE_PWD_MIN_LENGTH = 22;
|
||||
const size_t ICE_UFRAG_MAX_LENGTH = 256;
|
||||
const size_t ICE_PWD_MAX_LENGTH = 256;
|
||||
|
||||
// This is media-specific, so might belong
|
||||
// somewhere like media/base/mediaconstants.h
|
||||
const int ICE_CANDIDATE_COMPONENT_RTP = 1;
|
||||
const int ICE_CANDIDATE_COMPONENT_RTCP = 2;
|
||||
const int ICE_CANDIDATE_COMPONENT_DEFAULT = 1;
|
||||
|
||||
// From RFC 4145, SDP setup attribute values.
|
||||
const char CONNECTIONROLE_ACTIVE_STR[] = "active";
|
||||
const char CONNECTIONROLE_PASSIVE_STR[] = "passive";
|
||||
const char CONNECTIONROLE_ACTPASS_STR[] = "actpass";
|
||||
const char CONNECTIONROLE_HOLDCONN_STR[] = "holdconn";
|
||||
|
||||
const char LOCAL_TLD[] = ".local";
|
||||
|
||||
const int MIN_CHECK_RECEIVING_INTERVAL = 50;
|
||||
const int RECEIVING_TIMEOUT = MIN_CHECK_RECEIVING_INTERVAL * 50;
|
||||
const int RECEIVING_SWITCHING_DELAY = 1000;
|
||||
const int BACKUP_CONNECTION_PING_INTERVAL = 25 * 1000;
|
||||
const int REGATHER_ON_FAILED_NETWORKS_INTERVAL = 5 * 60 * 1000;
|
||||
|
||||
// When the socket is unwritable, we will use 10 Kbps (ignoring IP+UDP headers)
|
||||
// for pinging. When the socket is writable, we will use only 1 Kbps because we
|
||||
// don't want to degrade the quality on a modem. These numbers should work well
|
||||
// on a 28.8K modem, which is the slowest connection on which the voice quality
|
||||
// is reasonable at all.
|
||||
const int STUN_PING_PACKET_SIZE = 60 * 8;
|
||||
const int STRONG_PING_INTERVAL = 1000 * STUN_PING_PACKET_SIZE / 1000; // 480ms.
|
||||
const int WEAK_PING_INTERVAL = 1000 * STUN_PING_PACKET_SIZE / 10000; // 48ms.
|
||||
const int WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL = 900;
|
||||
const int STRONG_AND_STABLE_WRITABLE_CONNECTION_PING_INTERVAL = 2500;
|
||||
const int CONNECTION_WRITE_CONNECT_TIMEOUT = 5 * 1000; // 5 seconds
|
||||
const uint32_t CONNECTION_WRITE_CONNECT_FAILURES = 5; // 5 pings
|
||||
|
||||
const int STUN_KEEPALIVE_INTERVAL = 10 * 1000; // 10 seconds
|
||||
|
||||
const int MIN_CONNECTION_LIFETIME = 10 * 1000; // 10 seconds.
|
||||
const int DEAD_CONNECTION_RECEIVE_TIMEOUT = 30 * 1000; // 30 seconds.
|
||||
const int WEAK_CONNECTION_RECEIVE_TIMEOUT = 2500; // 2.5 seconds
|
||||
const int CONNECTION_WRITE_TIMEOUT = 15 * 1000; // 15 seconds
|
||||
// There is no harm to keep this value high other than a small amount
|
||||
// of increased memory, but in some networks (2G), we observe up to 60s RTTs.
|
||||
const int CONNECTION_RESPONSE_TIMEOUT = 60 * 1000; // 60 seconds
|
||||
|
||||
} // namespace cricket
|
||||
129
TMessagesProj/jni/voip/webrtc/p2p/base/p2p_constants.h
Normal file
129
TMessagesProj/jni/voip/webrtc/p2p/base/p2p_constants.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_P2P_CONSTANTS_H_
|
||||
#define P2P_BASE_P2P_CONSTANTS_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// CN_ == "content name". When we initiate a session, we choose the
|
||||
// name, and when we receive a Gingle session, we provide default
|
||||
// names (since Gingle has no content names). But when we receive a
|
||||
// Jingle call, the content name can be anything, so don't rely on
|
||||
// these values being the same as the ones received.
|
||||
extern const char CN_AUDIO[];
|
||||
extern const char CN_VIDEO[];
|
||||
extern const char CN_DATA[];
|
||||
extern const char CN_OTHER[];
|
||||
|
||||
// GN stands for group name
|
||||
extern const char GROUP_TYPE_BUNDLE[];
|
||||
|
||||
RTC_EXPORT extern const int ICE_UFRAG_LENGTH;
|
||||
RTC_EXPORT extern const int ICE_PWD_LENGTH;
|
||||
extern const size_t ICE_UFRAG_MIN_LENGTH;
|
||||
extern const size_t ICE_PWD_MIN_LENGTH;
|
||||
extern const size_t ICE_UFRAG_MAX_LENGTH;
|
||||
extern const size_t ICE_PWD_MAX_LENGTH;
|
||||
|
||||
RTC_EXPORT extern const int ICE_CANDIDATE_COMPONENT_RTP;
|
||||
RTC_EXPORT extern const int ICE_CANDIDATE_COMPONENT_RTCP;
|
||||
RTC_EXPORT extern const int ICE_CANDIDATE_COMPONENT_DEFAULT;
|
||||
|
||||
// RFC 4145, SDP setup attribute values.
|
||||
extern const char CONNECTIONROLE_ACTIVE_STR[];
|
||||
extern const char CONNECTIONROLE_PASSIVE_STR[];
|
||||
extern const char CONNECTIONROLE_ACTPASS_STR[];
|
||||
extern const char CONNECTIONROLE_HOLDCONN_STR[];
|
||||
|
||||
// RFC 6762, the .local pseudo-top-level domain used for mDNS names.
|
||||
extern const char LOCAL_TLD[];
|
||||
|
||||
// Constants for time intervals are in milliseconds unless otherwise stated.
|
||||
//
|
||||
// Most of the following constants are the default values of IceConfig
|
||||
// paramters. See IceConfig for detailed definition.
|
||||
//
|
||||
// Default value of IceConfig.receiving_timeout.
|
||||
extern const int RECEIVING_TIMEOUT;
|
||||
// Default value IceConfig.ice_check_min_interval.
|
||||
extern const int MIN_CHECK_RECEIVING_INTERVAL;
|
||||
// The next two ping intervals are at the ICE transport level.
|
||||
//
|
||||
// STRONG_PING_INTERVAL is applied when the selected connection is both
|
||||
// writable and receiving.
|
||||
//
|
||||
// Default value of IceConfig.ice_check_interval_strong_connectivity.
|
||||
extern const int STRONG_PING_INTERVAL;
|
||||
// WEAK_PING_INTERVAL is applied when the selected connection is either
|
||||
// not writable or not receiving.
|
||||
//
|
||||
// Defaul value of IceConfig.ice_check_interval_weak_connectivity.
|
||||
extern const int WEAK_PING_INTERVAL;
|
||||
// The next two ping intervals are at the candidate pair level.
|
||||
//
|
||||
// Writable candidate pairs are pinged at a slower rate once they are stabilized
|
||||
// and the channel is strongly connected.
|
||||
extern const int STRONG_AND_STABLE_WRITABLE_CONNECTION_PING_INTERVAL;
|
||||
// Writable candidate pairs are pinged at a faster rate while the connections
|
||||
// are stabilizing or the channel is weak.
|
||||
extern const int WEAK_OR_STABILIZING_WRITABLE_CONNECTION_PING_INTERVAL;
|
||||
// Default value of IceConfig.backup_connection_ping_interval
|
||||
extern const int BACKUP_CONNECTION_PING_INTERVAL;
|
||||
// Defualt value of IceConfig.receiving_switching_delay.
|
||||
extern const int RECEIVING_SWITCHING_DELAY;
|
||||
// Default value of IceConfig.regather_on_failed_networks_interval.
|
||||
extern const int REGATHER_ON_FAILED_NETWORKS_INTERVAL;
|
||||
// Default vaule of IceConfig.ice_unwritable_timeout.
|
||||
extern const int CONNECTION_WRITE_CONNECT_TIMEOUT;
|
||||
// Default vaule of IceConfig.ice_unwritable_min_checks.
|
||||
extern const uint32_t CONNECTION_WRITE_CONNECT_FAILURES;
|
||||
// Default value of IceConfig.ice_inactive_timeout;
|
||||
extern const int CONNECTION_WRITE_TIMEOUT;
|
||||
// Default value of IceConfig.stun_keepalive_interval;
|
||||
extern const int STUN_KEEPALIVE_INTERVAL;
|
||||
|
||||
static const int MIN_PINGS_AT_WEAK_PING_INTERVAL = 3;
|
||||
|
||||
// The following constants are used at the candidate pair level to determine the
|
||||
// state of a candidate pair.
|
||||
//
|
||||
// The timeout duration when a connection does not receive anything.
|
||||
extern const int WEAK_CONNECTION_RECEIVE_TIMEOUT;
|
||||
// A connection will be declared dead if it has not received anything for this
|
||||
// long.
|
||||
extern const int DEAD_CONNECTION_RECEIVE_TIMEOUT;
|
||||
// This is the length of time that we wait for a ping response to come back.
|
||||
extern const int CONNECTION_RESPONSE_TIMEOUT;
|
||||
// The minimum time we will wait before destroying a connection after creating
|
||||
// it.
|
||||
extern const int MIN_CONNECTION_LIFETIME;
|
||||
|
||||
// The type preference MUST be an integer from 0 to 126 inclusive.
|
||||
// https://datatracker.ietf.org/doc/html/rfc5245#section-4.1.2.1
|
||||
enum IcePriorityValue : uint8_t {
|
||||
ICE_TYPE_PREFERENCE_RELAY_TLS = 0,
|
||||
ICE_TYPE_PREFERENCE_RELAY_TCP = 1,
|
||||
ICE_TYPE_PREFERENCE_RELAY_UDP = 2,
|
||||
ICE_TYPE_PREFERENCE_PRFLX_TCP = 80,
|
||||
ICE_TYPE_PREFERENCE_HOST_TCP = 90,
|
||||
ICE_TYPE_PREFERENCE_SRFLX = 100,
|
||||
ICE_TYPE_PREFERENCE_PRFLX = 110,
|
||||
ICE_TYPE_PREFERENCE_HOST = 126
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_P2P_CONSTANTS_H_
|
||||
2326
TMessagesProj/jni/voip/webrtc/p2p/base/p2p_transport_channel.cc
Normal file
2326
TMessagesProj/jni/voip/webrtc/p2p/base/p2p_transport_channel.cc
Normal file
File diff suppressed because it is too large
Load diff
520
TMessagesProj/jni/voip/webrtc/p2p/base/p2p_transport_channel.h
Normal file
520
TMessagesProj/jni/voip/webrtc/p2p/base/p2p_transport_channel.h
Normal file
|
|
@ -0,0 +1,520 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// P2PTransportChannel wraps up the state management of the connection between
|
||||
// two P2P clients. Clients have candidate ports for connecting, and
|
||||
// connections which are combinations of candidates from each end (Alice and
|
||||
// Bob each have candidates, one candidate from Alice and one candidate from
|
||||
// Bob are used to make a connection, repeat to make many connections).
|
||||
//
|
||||
// When all of the available connections become invalid (non-writable), we
|
||||
// kick off a process of determining more candidates and more connections.
|
||||
//
|
||||
#ifndef P2P_BASE_P2P_TRANSPORT_CHANNEL_H_
|
||||
#define P2P_BASE_P2P_TRANSPORT_CHANNEL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/array_view.h"
|
||||
#include "api/async_dns_resolver.h"
|
||||
#include "api/candidate.h"
|
||||
#include "api/ice_transport_interface.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/transport/enums.h"
|
||||
#include "api/transport/stun.h"
|
||||
#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h"
|
||||
#include "logging/rtc_event_log/ice_logger.h"
|
||||
#include "p2p/base/active_ice_controller_factory_interface.h"
|
||||
#include "p2p/base/candidate_pair_interface.h"
|
||||
#include "p2p/base/connection.h"
|
||||
#include "p2p/base/ice_agent_interface.h"
|
||||
#include "p2p/base/ice_controller_factory_interface.h"
|
||||
#include "p2p/base/ice_controller_interface.h"
|
||||
#include "p2p/base/ice_switch_reason.h"
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "p2p/base/p2p_transport_channel_ice_field_trials.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "p2p/base/port_interface.h"
|
||||
#include "p2p/base/regathering_controller.h"
|
||||
#include "p2p/base/stun_dictionary.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/dscp.h"
|
||||
#include "rtc_base/network/sent_packet.h"
|
||||
#include "rtc_base/network_route.h"
|
||||
#include "rtc_base/socket.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace webrtc {
|
||||
class RtcEventLog;
|
||||
} // namespace webrtc
|
||||
|
||||
namespace cricket {
|
||||
|
||||
bool IceCredentialsChanged(absl::string_view old_ufrag,
|
||||
absl::string_view old_pwd,
|
||||
absl::string_view new_ufrag,
|
||||
absl::string_view new_pwd);
|
||||
|
||||
// Adds the port on which the candidate originated.
|
||||
class RemoteCandidate : public Candidate {
|
||||
public:
|
||||
RemoteCandidate(const Candidate& c, PortInterface* origin_port)
|
||||
: Candidate(c), origin_port_(origin_port) {}
|
||||
|
||||
PortInterface* origin_port() { return origin_port_; }
|
||||
|
||||
private:
|
||||
PortInterface* origin_port_;
|
||||
};
|
||||
|
||||
// P2PTransportChannel manages the candidates and connection process to keep
|
||||
// two P2P clients connected to each other.
|
||||
class RTC_EXPORT P2PTransportChannel : public IceTransportInternal,
|
||||
public IceAgentInterface {
|
||||
public:
|
||||
static std::unique_ptr<P2PTransportChannel> Create(
|
||||
absl::string_view transport_name,
|
||||
int component,
|
||||
webrtc::IceTransportInit init);
|
||||
|
||||
// For testing only.
|
||||
// TODO(zstein): Remove once AsyncDnsResolverFactory is required.
|
||||
P2PTransportChannel(absl::string_view transport_name,
|
||||
int component,
|
||||
PortAllocator* allocator,
|
||||
const webrtc::FieldTrialsView* field_trials = nullptr);
|
||||
|
||||
~P2PTransportChannel() override;
|
||||
|
||||
P2PTransportChannel(const P2PTransportChannel&) = delete;
|
||||
P2PTransportChannel& operator=(const P2PTransportChannel&) = delete;
|
||||
|
||||
// From TransportChannelImpl:
|
||||
IceTransportState GetState() const override;
|
||||
webrtc::IceTransportState GetIceTransportState() const override;
|
||||
|
||||
const std::string& transport_name() const override;
|
||||
int component() const override;
|
||||
bool writable() const override;
|
||||
bool receiving() const override;
|
||||
void SetIceRole(IceRole role) override;
|
||||
IceRole GetIceRole() const override;
|
||||
void SetIceTiebreaker(uint64_t tiebreaker) override;
|
||||
void SetIceParameters(const IceParameters& ice_params) override;
|
||||
void SetRemoteIceParameters(const IceParameters& ice_params) override;
|
||||
void SetRemoteIceMode(IceMode mode) override;
|
||||
// TODO(deadbeef): Deprecated. Remove when Chromium's
|
||||
// IceTransportChannel does not depend on this.
|
||||
void Connect() {}
|
||||
void MaybeStartGathering() override;
|
||||
IceGatheringState gathering_state() const override;
|
||||
void ResolveHostnameCandidate(const Candidate& candidate);
|
||||
void AddRemoteCandidate(const Candidate& candidate) override;
|
||||
void RemoveRemoteCandidate(const Candidate& candidate) override;
|
||||
void RemoveAllRemoteCandidates() override;
|
||||
// Sets the parameters in IceConfig. We do not set them blindly. Instead, we
|
||||
// only update the parameter if it is considered set in `config`. For example,
|
||||
// a negative value of receiving_timeout will be considered "not set" and we
|
||||
// will not use it to update the respective parameter in `config_`.
|
||||
// TODO(deadbeef): Use absl::optional instead of negative values.
|
||||
void SetIceConfig(const IceConfig& config) override;
|
||||
const IceConfig& config() const;
|
||||
static webrtc::RTCError ValidateIceConfig(const IceConfig& config);
|
||||
|
||||
// From TransportChannel:
|
||||
int SendPacket(const char* data,
|
||||
size_t len,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags) override;
|
||||
int SetOption(rtc::Socket::Option opt, int value) override;
|
||||
bool GetOption(rtc::Socket::Option opt, int* value) override;
|
||||
int GetError() override;
|
||||
bool GetStats(IceTransportStats* ice_transport_stats) override;
|
||||
absl::optional<int> GetRttEstimate() override;
|
||||
const Connection* selected_connection() const override;
|
||||
absl::optional<const CandidatePair> GetSelectedCandidatePair() const override;
|
||||
|
||||
// From IceAgentInterface
|
||||
void OnStartedPinging() override;
|
||||
int64_t GetLastPingSentMs() const override;
|
||||
void UpdateConnectionStates() override;
|
||||
void UpdateState() override;
|
||||
void SendPingRequest(const Connection* connection) override;
|
||||
void SwitchSelectedConnection(const Connection* connection,
|
||||
IceSwitchReason reason) override;
|
||||
void ForgetLearnedStateForConnections(
|
||||
rtc::ArrayView<const Connection* const> connections) override;
|
||||
bool PruneConnections(
|
||||
rtc::ArrayView<const Connection* const> connections) override;
|
||||
|
||||
// TODO(honghaiz): Remove this method once the reference of it in
|
||||
// Chromoting is removed.
|
||||
const Connection* best_connection() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return selected_connection_;
|
||||
}
|
||||
|
||||
void set_incoming_only(bool value) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
incoming_only_ = value;
|
||||
}
|
||||
|
||||
// Note: These are only for testing purpose.
|
||||
// `ports_` and `pruned_ports` should not be changed from outside.
|
||||
const std::vector<PortInterface*>& ports() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return ports_;
|
||||
}
|
||||
const std::vector<PortInterface*>& pruned_ports() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return pruned_ports_;
|
||||
}
|
||||
|
||||
IceMode remote_ice_mode() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return remote_ice_mode_;
|
||||
}
|
||||
|
||||
void PruneAllPorts();
|
||||
int check_receiving_interval() const;
|
||||
absl::optional<rtc::NetworkRoute> network_route() const override;
|
||||
|
||||
void RemoveConnection(Connection* connection);
|
||||
|
||||
// Helper method used only in unittest.
|
||||
rtc::DiffServCodePoint DefaultDscpValue() const;
|
||||
|
||||
// Public for unit tests.
|
||||
Connection* FindNextPingableConnection();
|
||||
void MarkConnectionPinged(Connection* conn);
|
||||
|
||||
// Public for unit tests.
|
||||
rtc::ArrayView<Connection* const> connections() const;
|
||||
void RemoveConnectionForTest(Connection* connection);
|
||||
|
||||
// Public for unit tests.
|
||||
PortAllocatorSession* allocator_session() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (allocator_sessions_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return allocator_sessions_.back().get();
|
||||
}
|
||||
|
||||
// Public for unit tests.
|
||||
const std::vector<RemoteCandidate>& remote_candidates() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return remote_candidates_;
|
||||
}
|
||||
|
||||
std::string ToString() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
const std::string RECEIVING_ABBREV[2] = {"_", "R"};
|
||||
const std::string WRITABLE_ABBREV[2] = {"_", "W"};
|
||||
rtc::StringBuilder ss;
|
||||
ss << "Channel[" << transport_name_ << "|" << component_ << "|"
|
||||
<< RECEIVING_ABBREV[receiving_] << WRITABLE_ABBREV[writable_] << "]";
|
||||
return ss.Release();
|
||||
}
|
||||
|
||||
absl::optional<std::reference_wrapper<StunDictionaryWriter>>
|
||||
GetDictionaryWriter() override {
|
||||
return stun_dict_writer_;
|
||||
}
|
||||
|
||||
private:
|
||||
P2PTransportChannel(
|
||||
absl::string_view transport_name,
|
||||
int component,
|
||||
PortAllocator* allocator,
|
||||
// DNS resolver factory
|
||||
webrtc::AsyncDnsResolverFactoryInterface* async_dns_resolver_factory,
|
||||
// If the P2PTransportChannel has to delete the DNS resolver factory
|
||||
// on release, this pointer is set.
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverFactoryInterface>
|
||||
owned_dns_resolver_factory,
|
||||
webrtc::RtcEventLog* event_log,
|
||||
IceControllerFactoryInterface* ice_controller_factory,
|
||||
ActiveIceControllerFactoryInterface* active_ice_controller_factory,
|
||||
const webrtc::FieldTrialsView* field_trials);
|
||||
|
||||
bool IsGettingPorts() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return allocator_session()->IsGettingPorts();
|
||||
}
|
||||
|
||||
// Returns true if it's possible to send packets on `connection`.
|
||||
bool ReadyToSend(const Connection* connection) const;
|
||||
bool PresumedWritable(const Connection* conn) const;
|
||||
void SendPingRequestInternal(Connection* connection);
|
||||
|
||||
rtc::NetworkRoute ConfigureNetworkRoute(const Connection* conn);
|
||||
void SwitchSelectedConnectionInternal(Connection* conn,
|
||||
IceSwitchReason reason);
|
||||
void UpdateTransportState();
|
||||
void HandleAllTimedOut();
|
||||
void MaybeStopPortAllocatorSessions();
|
||||
void OnSelectedConnectionDestroyed() RTC_RUN_ON(network_thread_);
|
||||
|
||||
// ComputeIceTransportState computes the RTCIceTransportState as described in
|
||||
// https://w3c.github.io/webrtc-pc/#dom-rtcicetransportstate. ComputeState
|
||||
// computes the value we currently export as RTCIceTransportState.
|
||||
// TODO(bugs.webrtc.org/9308): Remove ComputeState once it's no longer used.
|
||||
IceTransportState ComputeState() const;
|
||||
webrtc::IceTransportState ComputeIceTransportState() const;
|
||||
|
||||
bool CreateConnections(const Candidate& remote_candidate,
|
||||
PortInterface* origin_port);
|
||||
bool CreateConnection(PortInterface* port,
|
||||
const Candidate& remote_candidate,
|
||||
PortInterface* origin_port);
|
||||
bool FindConnection(const Connection* connection) const;
|
||||
|
||||
uint32_t GetRemoteCandidateGeneration(const Candidate& candidate);
|
||||
bool IsDuplicateRemoteCandidate(const Candidate& candidate);
|
||||
void RememberRemoteCandidate(const Candidate& remote_candidate,
|
||||
PortInterface* origin_port);
|
||||
void PingConnection(Connection* conn);
|
||||
void AddAllocatorSession(std::unique_ptr<PortAllocatorSession> session);
|
||||
void AddConnection(Connection* connection);
|
||||
|
||||
void OnPortReady(PortAllocatorSession* session, PortInterface* port);
|
||||
void OnPortsPruned(PortAllocatorSession* session,
|
||||
const std::vector<PortInterface*>& ports);
|
||||
void OnCandidatesReady(PortAllocatorSession* session,
|
||||
const std::vector<Candidate>& candidates);
|
||||
void OnCandidateError(PortAllocatorSession* session,
|
||||
const IceCandidateErrorEvent& event);
|
||||
void OnCandidatesRemoved(PortAllocatorSession* session,
|
||||
const std::vector<Candidate>& candidates);
|
||||
void OnCandidatesAllocationDone(PortAllocatorSession* session);
|
||||
void OnUnknownAddress(PortInterface* port,
|
||||
const rtc::SocketAddress& addr,
|
||||
ProtocolType proto,
|
||||
IceMessage* stun_msg,
|
||||
const std::string& remote_username,
|
||||
bool port_muxed);
|
||||
void OnCandidateFilterChanged(uint32_t prev_filter, uint32_t cur_filter);
|
||||
|
||||
// When a port is destroyed, remove it from both lists `ports_`
|
||||
// and `pruned_ports_`.
|
||||
void OnPortDestroyed(PortInterface* port);
|
||||
// When pruning a port, move it from `ports_` to `pruned_ports_`.
|
||||
// Returns true if the port is found and removed from `ports_`.
|
||||
bool PrunePort(PortInterface* port);
|
||||
void OnRoleConflict(PortInterface* port);
|
||||
|
||||
void OnConnectionStateChange(Connection* connection);
|
||||
void OnReadPacket(Connection* connection, const rtc::ReceivedPacket& packet);
|
||||
void OnSentPacket(const rtc::SentPacket& sent_packet);
|
||||
void OnReadyToSend(Connection* connection);
|
||||
void OnConnectionDestroyed(Connection* connection);
|
||||
|
||||
void OnNominated(Connection* conn);
|
||||
|
||||
void LogCandidatePairConfig(Connection* conn,
|
||||
webrtc::IceCandidatePairConfigType type);
|
||||
|
||||
uint32_t GetNominationAttr(Connection* conn) const;
|
||||
bool GetUseCandidateAttr(Connection* conn) const;
|
||||
|
||||
bool AllowedToPruneConnections() const;
|
||||
|
||||
// Returns the latest remote ICE parameters or nullptr if there are no remote
|
||||
// ICE parameters yet.
|
||||
IceParameters* remote_ice() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return remote_ice_parameters_.empty() ? nullptr
|
||||
: &remote_ice_parameters_.back();
|
||||
}
|
||||
// Returns the remote IceParameters and generation that match `ufrag`
|
||||
// if found, and returns nullptr otherwise.
|
||||
const IceParameters* FindRemoteIceFromUfrag(absl::string_view ufrag,
|
||||
uint32_t* generation);
|
||||
// Returns the index of the latest remote ICE parameters, or 0 if no remote
|
||||
// ICE parameters have been received.
|
||||
uint32_t remote_ice_generation() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return remote_ice_parameters_.empty()
|
||||
? 0
|
||||
: static_cast<uint32_t>(remote_ice_parameters_.size() - 1);
|
||||
}
|
||||
|
||||
// Indicates if the given local port has been pruned.
|
||||
bool IsPortPruned(const PortInterface* port) const;
|
||||
|
||||
// Indicates if the given remote candidate has been pruned.
|
||||
bool IsRemoteCandidatePruned(const Candidate& cand) const;
|
||||
|
||||
// Sets the writable state, signaling if necessary.
|
||||
void SetWritable(bool writable);
|
||||
// Sets the receiving state, signaling if necessary.
|
||||
void SetReceiving(bool receiving);
|
||||
// Clears the address and the related address fields of a local candidate to
|
||||
// avoid IP leakage. This is applicable in several scenarios as commented in
|
||||
// `PortAllocator::SanitizeCandidate`.
|
||||
Candidate SanitizeLocalCandidate(const Candidate& c) const;
|
||||
// Clears the address field of a remote candidate to avoid IP leakage. This is
|
||||
// applicable in the following scenarios:
|
||||
// 1. mDNS candidates are received.
|
||||
// 2. Peer-reflexive remote candidates.
|
||||
Candidate SanitizeRemoteCandidate(const Candidate& c) const;
|
||||
|
||||
// Cast a Connection returned from IceController and verify that it exists.
|
||||
// (P2P owns all Connections, and only gives const pointers to IceController,
|
||||
// see IceControllerInterface).
|
||||
Connection* FromIceController(const Connection* conn) {
|
||||
// Verify that IceController does not return a connection
|
||||
// that we have destroyed.
|
||||
RTC_DCHECK(FindConnection(conn));
|
||||
return const_cast<Connection*>(conn);
|
||||
}
|
||||
|
||||
int64_t ComputeEstimatedDisconnectedTimeMs(int64_t now,
|
||||
Connection* old_connection);
|
||||
|
||||
void ParseFieldTrials(const webrtc::FieldTrialsView* field_trials);
|
||||
|
||||
std::string transport_name_ RTC_GUARDED_BY(network_thread_);
|
||||
int component_ RTC_GUARDED_BY(network_thread_);
|
||||
PortAllocator* allocator_ RTC_GUARDED_BY(network_thread_);
|
||||
webrtc::AsyncDnsResolverFactoryInterface* const async_dns_resolver_factory_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
const std::unique_ptr<webrtc::AsyncDnsResolverFactoryInterface>
|
||||
owned_dns_resolver_factory_;
|
||||
rtc::Thread* const network_thread_;
|
||||
bool incoming_only_ RTC_GUARDED_BY(network_thread_);
|
||||
int error_ RTC_GUARDED_BY(network_thread_);
|
||||
std::vector<std::unique_ptr<PortAllocatorSession>> allocator_sessions_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
// `ports_` contains ports that are used to form new connections when
|
||||
// new remote candidates are added.
|
||||
std::vector<PortInterface*> ports_ RTC_GUARDED_BY(network_thread_);
|
||||
// `pruned_ports_` contains ports that have been removed from `ports_` and
|
||||
// are not being used to form new connections, but that aren't yet destroyed.
|
||||
// They may have existing connections, and they still fire signals such as
|
||||
// SignalUnknownAddress.
|
||||
std::vector<PortInterface*> pruned_ports_ RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
Connection* selected_connection_ RTC_GUARDED_BY(network_thread_) = nullptr;
|
||||
std::vector<Connection*> connections_ RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
std::vector<RemoteCandidate> remote_candidates_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
bool had_connection_ RTC_GUARDED_BY(network_thread_) =
|
||||
false; // if connections_ has ever been nonempty
|
||||
typedef std::map<rtc::Socket::Option, int> OptionMap;
|
||||
OptionMap options_ RTC_GUARDED_BY(network_thread_);
|
||||
IceParameters ice_parameters_ RTC_GUARDED_BY(network_thread_);
|
||||
std::vector<IceParameters> remote_ice_parameters_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
IceMode remote_ice_mode_ RTC_GUARDED_BY(network_thread_);
|
||||
IceRole ice_role_ RTC_GUARDED_BY(network_thread_);
|
||||
uint64_t ice_tiebreaker_ RTC_GUARDED_BY(network_thread_);
|
||||
IceGatheringState gathering_state_ RTC_GUARDED_BY(network_thread_);
|
||||
std::unique_ptr<webrtc::BasicRegatheringController> regathering_controller_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
int64_t last_ping_sent_ms_ RTC_GUARDED_BY(network_thread_) = 0;
|
||||
int weak_ping_interval_ RTC_GUARDED_BY(network_thread_) = WEAK_PING_INTERVAL;
|
||||
// TODO(jonasolsson): Remove state_ and rename standardized_state_ once state_
|
||||
// is no longer used to compute the ICE connection state.
|
||||
IceTransportState state_ RTC_GUARDED_BY(network_thread_) =
|
||||
IceTransportState::STATE_INIT;
|
||||
webrtc::IceTransportState standardized_state_
|
||||
RTC_GUARDED_BY(network_thread_) = webrtc::IceTransportState::kNew;
|
||||
IceConfig config_ RTC_GUARDED_BY(network_thread_);
|
||||
int last_sent_packet_id_ RTC_GUARDED_BY(network_thread_) =
|
||||
-1; // -1 indicates no packet was sent before.
|
||||
// The value put in the "nomination" attribute for the next nominated
|
||||
// connection. A zero-value indicates the connection will not be nominated.
|
||||
uint32_t nomination_ RTC_GUARDED_BY(network_thread_) = 0;
|
||||
bool receiving_ RTC_GUARDED_BY(network_thread_) = false;
|
||||
bool writable_ RTC_GUARDED_BY(network_thread_) = false;
|
||||
bool has_been_writable_ RTC_GUARDED_BY(network_thread_) =
|
||||
false; // if writable_ has ever been true
|
||||
|
||||
absl::optional<rtc::NetworkRoute> network_route_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
webrtc::IceEventLog ice_event_log_ RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
std::unique_ptr<ActiveIceControllerInterface> ice_controller_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
|
||||
struct CandidateAndResolver final {
|
||||
CandidateAndResolver(
|
||||
const Candidate& candidate,
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface>&& resolver);
|
||||
~CandidateAndResolver();
|
||||
// Moveable, but not copyable.
|
||||
CandidateAndResolver(CandidateAndResolver&&) = default;
|
||||
CandidateAndResolver& operator=(CandidateAndResolver&&) = default;
|
||||
|
||||
Candidate candidate_;
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface> resolver_;
|
||||
};
|
||||
std::vector<CandidateAndResolver> resolvers_ RTC_GUARDED_BY(network_thread_);
|
||||
void FinishAddingRemoteCandidate(const Candidate& new_remote_candidate);
|
||||
void OnCandidateResolved(webrtc::AsyncDnsResolverInterface* resolver);
|
||||
void AddRemoteCandidateWithResult(
|
||||
Candidate candidate,
|
||||
const webrtc::AsyncDnsResolverResult& result);
|
||||
|
||||
std::unique_ptr<StunAttribute> GoogDeltaReceived(
|
||||
const StunByteStringAttribute*);
|
||||
void GoogDeltaAckReceived(webrtc::RTCErrorOr<const StunUInt64Attribute*>);
|
||||
|
||||
// Bytes/packets sent/received on this channel.
|
||||
uint64_t bytes_sent_ = 0;
|
||||
uint64_t bytes_received_ = 0;
|
||||
uint64_t packets_sent_ = 0;
|
||||
uint64_t packets_received_ = 0;
|
||||
|
||||
// Number of times the selected_connection_ has been modified.
|
||||
uint32_t selected_candidate_pair_changes_ = 0;
|
||||
|
||||
// When was last data received on a existing connection,
|
||||
// from connection->last_data_received() that uses rtc::TimeMillis().
|
||||
int64_t last_data_received_ms_ = 0;
|
||||
|
||||
// Parsed field trials.
|
||||
IceFieldTrials ice_field_trials_;
|
||||
|
||||
// A dictionary of attributes that will be reflected to peer.
|
||||
StunDictionaryWriter stun_dict_writer_;
|
||||
|
||||
// A dictionary that tracks attributes from peer.
|
||||
StunDictionaryView stun_dict_view_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_P2P_TRANSPORT_CHANNEL_H_
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2019 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_P2P_TRANSPORT_CHANNEL_ICE_FIELD_TRIALS_H_
|
||||
#define P2P_BASE_P2P_TRANSPORT_CHANNEL_ICE_FIELD_TRIALS_H_
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// Field trials for P2PTransportChannel and friends,
|
||||
// put in separate file so that they can be shared e.g
|
||||
// with Connection.
|
||||
struct IceFieldTrials {
|
||||
// This struct is built using the FieldTrialParser, and then not modified.
|
||||
// TODO(jonaso) : Consider how members of this struct can be made const.
|
||||
|
||||
bool skip_relay_to_non_relay_connections = false;
|
||||
absl::optional<int> max_outstanding_pings;
|
||||
|
||||
// Wait X ms before selecting a connection when having none.
|
||||
// This will make media slower, but will give us chance to find
|
||||
// a better connection before starting.
|
||||
absl::optional<int> initial_select_dampening;
|
||||
|
||||
// If the connection has recevied a ping-request, delay by
|
||||
// maximum this delay. This will make media slower, but will
|
||||
// give us chance to find a better connection before starting.
|
||||
absl::optional<int> initial_select_dampening_ping_received;
|
||||
|
||||
// Announce GOOG_PING support in STUN_BINDING_RESPONSE if requested
|
||||
// by peer.
|
||||
bool announce_goog_ping = true;
|
||||
|
||||
// Enable sending GOOG_PING if remote announce it.
|
||||
bool enable_goog_ping = false;
|
||||
|
||||
// Decay rate for RTT estimate using EventBasedExponentialMovingAverage
|
||||
// expressed as halving time.
|
||||
int rtt_estimate_halftime_ms = 500;
|
||||
|
||||
// Sending a PING directly after a switch on ICE_CONTROLLING-side.
|
||||
// TODO(jonaso) : Deprecate this in favor of
|
||||
// `send_ping_on_selected_ice_controlling`.
|
||||
bool send_ping_on_switch_ice_controlling = false;
|
||||
|
||||
// Sending a PING directly after selecting a connection
|
||||
// (i.e either a switch or the inital selection).
|
||||
bool send_ping_on_selected_ice_controlling = false;
|
||||
|
||||
// Sending a PING directly after a nomination on ICE_CONTROLLED-side.
|
||||
bool send_ping_on_nomination_ice_controlled = false;
|
||||
|
||||
// The timeout after which the connection will be considered dead if no
|
||||
// traffic is received.
|
||||
int dead_connection_timeout_ms = 30000;
|
||||
|
||||
// Stop gathering when having a strong connection.
|
||||
bool stop_gather_on_strongly_connected = true;
|
||||
|
||||
// DSCP taging.
|
||||
absl::optional<int> override_dscp;
|
||||
|
||||
bool piggyback_ice_check_acknowledgement = false;
|
||||
bool extra_ice_ping = false;
|
||||
|
||||
// Announce/enable GOOG_DELTA
|
||||
bool enable_goog_delta = true; // send GOOG DELTA
|
||||
bool answer_goog_delta = true; // answer GOOG DELTA
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_P2P_TRANSPORT_CHANNEL_ICE_FIELD_TRIALS_H_
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2017 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/packet_transport_internal.h"
|
||||
|
||||
namespace rtc {
|
||||
|
||||
PacketTransportInternal::PacketTransportInternal() = default;
|
||||
|
||||
PacketTransportInternal::~PacketTransportInternal() = default;
|
||||
|
||||
bool PacketTransportInternal::GetOption(rtc::Socket::Option opt, int* value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
absl::optional<NetworkRoute> PacketTransportInternal::network_route() const {
|
||||
return absl::optional<NetworkRoute>();
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright 2017 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_PACKET_TRANSPORT_INTERNAL_H_
|
||||
#define P2P_BASE_PACKET_TRANSPORT_INTERNAL_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/network_route.h"
|
||||
#include "rtc_base/socket.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
|
||||
namespace rtc {
|
||||
struct PacketOptions;
|
||||
struct SentPacket;
|
||||
|
||||
class RTC_EXPORT PacketTransportInternal : public sigslot::has_slots<> {
|
||||
public:
|
||||
virtual const std::string& transport_name() const = 0;
|
||||
|
||||
// The transport has been established.
|
||||
virtual bool writable() const = 0;
|
||||
|
||||
// The transport has received a packet in the last X milliseconds, here X is
|
||||
// configured by each implementation.
|
||||
virtual bool receiving() const = 0;
|
||||
|
||||
// Attempts to send the given packet.
|
||||
// The return value is < 0 on failure. The return value in failure case is not
|
||||
// descriptive. Depending on failure cause and implementation details
|
||||
// GetError() returns an descriptive errno.h error value.
|
||||
// This mimics posix socket send() or sendto() behavior.
|
||||
// TODO(johan): Reliable, meaningful, consistent error codes for all
|
||||
// implementations would be nice.
|
||||
// TODO(johan): Remove the default argument once channel code is updated.
|
||||
virtual int SendPacket(const char* data,
|
||||
size_t len,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags = 0) = 0;
|
||||
|
||||
// Sets a socket option. Note that not all options are
|
||||
// supported by all transport types.
|
||||
virtual int SetOption(rtc::Socket::Option opt, int value) = 0;
|
||||
|
||||
// TODO(pthatcher): Once Chrome's MockPacketTransportInterface implements
|
||||
// this, remove the default implementation.
|
||||
virtual bool GetOption(rtc::Socket::Option opt, int* value);
|
||||
|
||||
// Returns the most recent error that occurred on this channel.
|
||||
virtual int GetError() = 0;
|
||||
|
||||
// Returns the current network route with transport overhead.
|
||||
// TODO(zhihuang): Make it pure virtual once the Chrome/remoting is updated.
|
||||
virtual absl::optional<NetworkRoute> network_route() const;
|
||||
|
||||
// Emitted when the writable state, represented by `writable()`, changes.
|
||||
sigslot::signal1<PacketTransportInternal*> SignalWritableState;
|
||||
|
||||
// Emitted when the PacketTransportInternal is ready to send packets. "Ready
|
||||
// to send" is more sensitive than the writable state; a transport may be
|
||||
// writable, but temporarily not able to send packets. For example, the
|
||||
// underlying transport's socket buffer may be full, as indicated by
|
||||
// SendPacket's return code and/or GetError.
|
||||
sigslot::signal1<PacketTransportInternal*> SignalReadyToSend;
|
||||
|
||||
// Emitted when receiving state changes to true.
|
||||
sigslot::signal1<PacketTransportInternal*> SignalReceivingState;
|
||||
|
||||
// Signalled each time a packet is received on this channel.
|
||||
sigslot::signal5<PacketTransportInternal*,
|
||||
const char*,
|
||||
size_t,
|
||||
// TODO(bugs.webrtc.org/9584): Change to passing the int64_t
|
||||
// timestamp by value.
|
||||
const int64_t&,
|
||||
int>
|
||||
SignalReadPacket;
|
||||
|
||||
// Signalled each time a packet is sent on this channel.
|
||||
sigslot::signal2<PacketTransportInternal*, const rtc::SentPacket&>
|
||||
SignalSentPacket;
|
||||
|
||||
// Signalled when the current network route has changed.
|
||||
sigslot::signal1<absl::optional<rtc::NetworkRoute>> SignalNetworkRouteChanged;
|
||||
|
||||
// Signalled when the transport is closed.
|
||||
sigslot::signal1<PacketTransportInternal*> SignalClosed;
|
||||
|
||||
protected:
|
||||
PacketTransportInternal();
|
||||
~PacketTransportInternal() override;
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif // P2P_BASE_PACKET_TRANSPORT_INTERNAL_H_
|
||||
974
TMessagesProj/jni/voip/webrtc/p2p/base/port.cc
Normal file
974
TMessagesProj/jni/voip/webrtc/p2p/base/port.cc
Normal file
|
|
@ -0,0 +1,974 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/port.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/array_view.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "p2p/base/stun_request.h"
|
||||
#include "rtc_base/byte_buffer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/crc32.h"
|
||||
#include "rtc_base/helpers.h"
|
||||
#include "rtc_base/ip_address.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/mdns_responder_interface.h"
|
||||
#include "rtc_base/net_helper.h"
|
||||
#include "rtc_base/network.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/string_encode.h"
|
||||
#include "rtc_base/string_utils.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
#include "rtc_base/trace_event.h"
|
||||
|
||||
namespace cricket {
|
||||
namespace {
|
||||
|
||||
using ::webrtc::RTCError;
|
||||
using ::webrtc::RTCErrorType;
|
||||
using ::webrtc::TaskQueueBase;
|
||||
using ::webrtc::TimeDelta;
|
||||
|
||||
rtc::PacketInfoProtocolType ConvertProtocolTypeToPacketInfoProtocolType(
|
||||
cricket::ProtocolType type) {
|
||||
switch (type) {
|
||||
case cricket::ProtocolType::PROTO_UDP:
|
||||
return rtc::PacketInfoProtocolType::kUdp;
|
||||
case cricket::ProtocolType::PROTO_TCP:
|
||||
return rtc::PacketInfoProtocolType::kTcp;
|
||||
case cricket::ProtocolType::PROTO_SSLTCP:
|
||||
return rtc::PacketInfoProtocolType::kSsltcp;
|
||||
case cricket::ProtocolType::PROTO_TLS:
|
||||
return rtc::PacketInfoProtocolType::kTls;
|
||||
default:
|
||||
return rtc::PacketInfoProtocolType::kUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
// The delay before we begin checking if this port is useless. We set
|
||||
// it to a little higher than a total STUN timeout.
|
||||
const int kPortTimeoutDelay = cricket::STUN_TOTAL_TIMEOUT + 5000;
|
||||
|
||||
} // namespace
|
||||
|
||||
static const char* const PROTO_NAMES[] = {UDP_PROTOCOL_NAME, TCP_PROTOCOL_NAME,
|
||||
SSLTCP_PROTOCOL_NAME,
|
||||
TLS_PROTOCOL_NAME};
|
||||
|
||||
const char* ProtoToString(ProtocolType proto) {
|
||||
return PROTO_NAMES[proto];
|
||||
}
|
||||
|
||||
absl::optional<ProtocolType> StringToProto(absl::string_view proto_name) {
|
||||
for (size_t i = 0; i <= PROTO_LAST; ++i) {
|
||||
if (absl::EqualsIgnoreCase(PROTO_NAMES[i], proto_name)) {
|
||||
return static_cast<ProtocolType>(i);
|
||||
}
|
||||
}
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
// RFC 6544, TCP candidate encoding rules.
|
||||
const int DISCARD_PORT = 9;
|
||||
const char TCPTYPE_ACTIVE_STR[] = "active";
|
||||
const char TCPTYPE_PASSIVE_STR[] = "passive";
|
||||
const char TCPTYPE_SIMOPEN_STR[] = "so";
|
||||
|
||||
std::string Port::ComputeFoundation(absl::string_view type,
|
||||
absl::string_view protocol,
|
||||
absl::string_view relay_protocol,
|
||||
const rtc::SocketAddress& base_address) {
|
||||
// TODO(bugs.webrtc.org/14605): ensure IceTiebreaker() is set.
|
||||
rtc::StringBuilder sb;
|
||||
sb << type << base_address.ipaddr().ToString() << protocol << relay_protocol
|
||||
<< rtc::ToString(IceTiebreaker());
|
||||
return rtc::ToString(rtc::ComputeCrc32(sb.Release()));
|
||||
}
|
||||
|
||||
Port::Port(TaskQueueBase* thread,
|
||||
absl::string_view type,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
absl::string_view username_fragment,
|
||||
absl::string_view password,
|
||||
const webrtc::FieldTrialsView* field_trials)
|
||||
: thread_(thread),
|
||||
factory_(factory),
|
||||
type_(type),
|
||||
send_retransmit_count_attribute_(false),
|
||||
network_(network),
|
||||
min_port_(0),
|
||||
max_port_(0),
|
||||
component_(ICE_CANDIDATE_COMPONENT_DEFAULT),
|
||||
generation_(0),
|
||||
ice_username_fragment_(username_fragment),
|
||||
password_(password),
|
||||
timeout_delay_(kPortTimeoutDelay),
|
||||
enable_port_packets_(false),
|
||||
ice_role_(ICEROLE_UNKNOWN),
|
||||
tiebreaker_(0),
|
||||
shared_socket_(true),
|
||||
weak_factory_(this),
|
||||
field_trials_(field_trials) {
|
||||
RTC_DCHECK(factory_ != NULL);
|
||||
Construct();
|
||||
}
|
||||
|
||||
Port::Port(TaskQueueBase* thread,
|
||||
absl::string_view type,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
absl::string_view username_fragment,
|
||||
absl::string_view password,
|
||||
const webrtc::FieldTrialsView* field_trials)
|
||||
: thread_(thread),
|
||||
factory_(factory),
|
||||
type_(type),
|
||||
send_retransmit_count_attribute_(false),
|
||||
network_(network),
|
||||
min_port_(min_port),
|
||||
max_port_(max_port),
|
||||
component_(ICE_CANDIDATE_COMPONENT_DEFAULT),
|
||||
generation_(0),
|
||||
ice_username_fragment_(username_fragment),
|
||||
password_(password),
|
||||
timeout_delay_(kPortTimeoutDelay),
|
||||
enable_port_packets_(false),
|
||||
ice_role_(ICEROLE_UNKNOWN),
|
||||
tiebreaker_(0),
|
||||
shared_socket_(false),
|
||||
weak_factory_(this),
|
||||
field_trials_(field_trials) {
|
||||
RTC_DCHECK(factory_ != NULL);
|
||||
Construct();
|
||||
}
|
||||
|
||||
void Port::Construct() {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
// TODO(pthatcher): Remove this old behavior once we're sure no one
|
||||
// relies on it. If the username_fragment and password are empty,
|
||||
// we should just create one.
|
||||
if (ice_username_fragment_.empty()) {
|
||||
RTC_DCHECK(password_.empty());
|
||||
ice_username_fragment_ = rtc::CreateRandomString(ICE_UFRAG_LENGTH);
|
||||
password_ = rtc::CreateRandomString(ICE_PWD_LENGTH);
|
||||
}
|
||||
network_->SignalTypeChanged.connect(this, &Port::OnNetworkTypeChanged);
|
||||
network_cost_ = network_->GetCost(field_trials());
|
||||
|
||||
PostDestroyIfDead(/*delayed=*/true);
|
||||
RTC_LOG(LS_INFO) << ToString() << ": Port created with network cost "
|
||||
<< network_cost_;
|
||||
}
|
||||
|
||||
Port::~Port() {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
DestroyAllConnections();
|
||||
CancelPendingTasks();
|
||||
}
|
||||
|
||||
const absl::string_view Port::Type() const {
|
||||
return type_;
|
||||
}
|
||||
const rtc::Network* Port::Network() const {
|
||||
return network_;
|
||||
}
|
||||
|
||||
IceRole Port::GetIceRole() const {
|
||||
return ice_role_;
|
||||
}
|
||||
|
||||
void Port::SetIceRole(IceRole role) {
|
||||
ice_role_ = role;
|
||||
}
|
||||
|
||||
void Port::SetIceTiebreaker(uint64_t tiebreaker) {
|
||||
tiebreaker_ = tiebreaker;
|
||||
}
|
||||
|
||||
uint64_t Port::IceTiebreaker() const {
|
||||
return tiebreaker_;
|
||||
}
|
||||
|
||||
bool Port::SharedSocket() const {
|
||||
return shared_socket_;
|
||||
}
|
||||
|
||||
void Port::SetIceParameters(int component,
|
||||
absl::string_view username_fragment,
|
||||
absl::string_view password) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
component_ = component;
|
||||
ice_username_fragment_ = std::string(username_fragment);
|
||||
password_ = std::string(password);
|
||||
for (Candidate& c : candidates_) {
|
||||
c.set_component(component);
|
||||
c.set_username(username_fragment);
|
||||
c.set_password(password);
|
||||
}
|
||||
|
||||
// In case any connections exist make sure we update them too.
|
||||
for (auto& [unused, connection] : connections_) {
|
||||
connection->UpdateLocalIceParameters(component, username_fragment,
|
||||
password);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<Candidate>& Port::Candidates() const {
|
||||
return candidates_;
|
||||
}
|
||||
|
||||
Connection* Port::GetConnection(const rtc::SocketAddress& remote_addr) {
|
||||
AddressMap::const_iterator iter = connections_.find(remote_addr);
|
||||
if (iter != connections_.end())
|
||||
return iter->second;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Port::AddAddress(const rtc::SocketAddress& address,
|
||||
const rtc::SocketAddress& base_address,
|
||||
const rtc::SocketAddress& related_address,
|
||||
absl::string_view protocol,
|
||||
absl::string_view relay_protocol,
|
||||
absl::string_view tcptype,
|
||||
absl::string_view type,
|
||||
uint32_t type_preference,
|
||||
uint32_t relay_preference,
|
||||
absl::string_view url,
|
||||
bool is_final) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
|
||||
std::string foundation =
|
||||
ComputeFoundation(type, protocol, relay_protocol, base_address);
|
||||
Candidate c(component_, protocol, address, 0U, username_fragment(), password_,
|
||||
type, generation_, foundation, network_->id(), network_cost_);
|
||||
|
||||
#if RTC_DCHECK_IS_ON
|
||||
if (protocol == TCP_PROTOCOL_NAME && c.is_local()) {
|
||||
RTC_DCHECK(!tcptype.empty());
|
||||
}
|
||||
#endif
|
||||
|
||||
c.set_relay_protocol(relay_protocol);
|
||||
c.set_priority(
|
||||
c.GetPriority(type_preference, network_->preference(), relay_preference,
|
||||
field_trials_->IsEnabled(
|
||||
"WebRTC-IncreaseIceCandidatePriorityHostSrflx")));
|
||||
c.set_tcptype(tcptype);
|
||||
c.set_network_name(network_->name());
|
||||
c.set_network_type(network_->type());
|
||||
c.set_underlying_type_for_vpn(network_->underlying_type_for_vpn());
|
||||
c.set_url(url);
|
||||
c.set_related_address(related_address);
|
||||
|
||||
bool pending = MaybeObfuscateAddress(c, is_final);
|
||||
|
||||
if (!pending) {
|
||||
FinishAddingAddress(c, is_final);
|
||||
}
|
||||
}
|
||||
|
||||
bool Port::MaybeObfuscateAddress(const Candidate& c, bool is_final) {
|
||||
// TODO(bugs.webrtc.org/9723): Use a config to control the feature of IP
|
||||
// handling with mDNS.
|
||||
if (network_->GetMdnsResponder() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (!c.is_local()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto copy = c;
|
||||
auto weak_ptr = weak_factory_.GetWeakPtr();
|
||||
auto callback = [weak_ptr, copy, is_final](const rtc::IPAddress& addr,
|
||||
absl::string_view name) mutable {
|
||||
RTC_DCHECK(copy.address().ipaddr() == addr);
|
||||
rtc::SocketAddress hostname_address(name, copy.address().port());
|
||||
// In Port and Connection, we need the IP address information to
|
||||
// correctly handle the update of candidate type to prflx. The removal
|
||||
// of IP address when signaling this candidate will take place in
|
||||
// BasicPortAllocatorSession::OnCandidateReady, via SanitizeCandidate.
|
||||
hostname_address.SetResolvedIP(addr);
|
||||
copy.set_address(hostname_address);
|
||||
copy.set_related_address(rtc::SocketAddress());
|
||||
if (weak_ptr != nullptr) {
|
||||
RTC_DCHECK_RUN_ON(weak_ptr->thread_);
|
||||
weak_ptr->set_mdns_name_registration_status(
|
||||
MdnsNameRegistrationStatus::kCompleted);
|
||||
weak_ptr->FinishAddingAddress(copy, is_final);
|
||||
}
|
||||
};
|
||||
set_mdns_name_registration_status(MdnsNameRegistrationStatus::kInProgress);
|
||||
network_->GetMdnsResponder()->CreateNameForAddress(copy.address().ipaddr(),
|
||||
callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Port::FinishAddingAddress(const Candidate& c, bool is_final) {
|
||||
candidates_.push_back(c);
|
||||
SignalCandidateReady(this, c);
|
||||
|
||||
PostAddAddress(is_final);
|
||||
}
|
||||
|
||||
void Port::PostAddAddress(bool is_final) {
|
||||
if (is_final) {
|
||||
SignalPortComplete(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Port::AddOrReplaceConnection(Connection* conn) {
|
||||
auto ret = connections_.insert(
|
||||
std::make_pair(conn->remote_candidate().address(), conn));
|
||||
// If there is a different connection on the same remote address, replace
|
||||
// it with the new one and destroy the old one.
|
||||
if (ret.second == false && ret.first->second != conn) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< ToString()
|
||||
<< ": A new connection was created on an existing remote address. "
|
||||
"New remote candidate: "
|
||||
<< conn->remote_candidate().ToSensitiveString();
|
||||
std::unique_ptr<Connection> old_conn = absl::WrapUnique(ret.first->second);
|
||||
ret.first->second = conn;
|
||||
HandleConnectionDestroyed(old_conn.get());
|
||||
old_conn->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void Port::OnReadPacket(const rtc::ReceivedPacket& packet, ProtocolType proto) {
|
||||
const char* data = reinterpret_cast<const char*>(packet.payload().data());
|
||||
size_t size = packet.payload().size();
|
||||
const rtc::SocketAddress& addr = packet.source_address();
|
||||
// If the user has enabled port packets, just hand this over.
|
||||
if (enable_port_packets_) {
|
||||
SignalReadPacket(this, data, size, addr);
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is an authenticated STUN request, then signal unknown address and
|
||||
// send back a proper binding response.
|
||||
std::unique_ptr<IceMessage> msg;
|
||||
std::string remote_username;
|
||||
if (!GetStunMessage(data, size, addr, &msg, &remote_username)) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Received non-STUN packet from unknown address: "
|
||||
<< addr.ToSensitiveString();
|
||||
} else if (!msg) {
|
||||
// STUN message handled already
|
||||
} else if (msg->type() == STUN_BINDING_REQUEST) {
|
||||
RTC_LOG(LS_INFO) << "Received " << StunMethodToString(msg->type())
|
||||
<< " id=" << rtc::hex_encode(msg->transaction_id())
|
||||
<< " from unknown address " << addr.ToSensitiveString();
|
||||
// We need to signal an unknown address before we handle any role conflict
|
||||
// below. Otherwise there would be no candidate pair and TURN entry created
|
||||
// to send the error response in case of a role conflict.
|
||||
SignalUnknownAddress(this, addr, proto, msg.get(), remote_username, false);
|
||||
// Check for role conflicts.
|
||||
if (!MaybeIceRoleConflict(addr, msg.get(), remote_username)) {
|
||||
RTC_LOG(LS_INFO) << "Received conflicting role from the peer.";
|
||||
return;
|
||||
}
|
||||
} else if (msg->type() == GOOG_PING_REQUEST) {
|
||||
// This is a PING sent to a connection that was destroyed.
|
||||
// Send back that this is the case and a authenticated BINDING
|
||||
// is needed.
|
||||
SendBindingErrorResponse(msg.get(), addr, STUN_ERROR_BAD_REQUEST,
|
||||
STUN_ERROR_REASON_BAD_REQUEST);
|
||||
} else {
|
||||
// NOTE(tschmelcher): STUN_BINDING_RESPONSE is benign. It occurs if we
|
||||
// pruned a connection for this port while it had STUN requests in flight,
|
||||
// because we then get back responses for them, which this code correctly
|
||||
// does not handle.
|
||||
if (msg->type() != STUN_BINDING_RESPONSE &&
|
||||
msg->type() != GOOG_PING_RESPONSE &&
|
||||
msg->type() != GOOG_PING_ERROR_RESPONSE) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Received unexpected STUN message type: "
|
||||
<< msg->type() << " from unknown address: "
|
||||
<< addr.ToSensitiveString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Port::OnReadyToSend() {
|
||||
AddressMap::iterator iter = connections_.begin();
|
||||
for (; iter != connections_.end(); ++iter) {
|
||||
iter->second->OnReadyToSend();
|
||||
}
|
||||
}
|
||||
|
||||
void Port::AddPrflxCandidate(const Candidate& local) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
candidates_.push_back(local);
|
||||
}
|
||||
|
||||
bool Port::GetStunMessage(const char* data,
|
||||
size_t size,
|
||||
const rtc::SocketAddress& addr,
|
||||
std::unique_ptr<IceMessage>* out_msg,
|
||||
std::string* out_username) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
// NOTE: This could clearly be optimized to avoid allocating any memory.
|
||||
// However, at the data rates we'll be looking at on the client side,
|
||||
// this probably isn't worth worrying about.
|
||||
RTC_DCHECK(out_msg != NULL);
|
||||
RTC_DCHECK(out_username != NULL);
|
||||
out_username->clear();
|
||||
|
||||
// Don't bother parsing the packet if we can tell it's not STUN.
|
||||
// In ICE mode, all STUN packets will have a valid fingerprint.
|
||||
// Except GOOG_PING_REQUEST/RESPONSE that does not send fingerprint.
|
||||
int types[] = {GOOG_PING_REQUEST, GOOG_PING_RESPONSE,
|
||||
GOOG_PING_ERROR_RESPONSE};
|
||||
if (!StunMessage::IsStunMethod(types, data, size) &&
|
||||
!StunMessage::ValidateFingerprint(data, size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the request message. If the packet is not a complete and correct
|
||||
// STUN message, then ignore it.
|
||||
std::unique_ptr<IceMessage> stun_msg(new IceMessage());
|
||||
rtc::ByteBufferReader buf(
|
||||
rtc::MakeArrayView(reinterpret_cast<const uint8_t*>(data), size));
|
||||
if (!stun_msg->Read(&buf) || (buf.Length() > 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get list of attributes in the "comprehension-required" range that were not
|
||||
// comprehended. If one or more is found, the behavior differs based on the
|
||||
// type of the incoming message; see below.
|
||||
std::vector<uint16_t> unknown_attributes =
|
||||
stun_msg->GetNonComprehendedAttributes();
|
||||
|
||||
if (stun_msg->type() == STUN_BINDING_REQUEST) {
|
||||
// Check for the presence of USERNAME and MESSAGE-INTEGRITY (if ICE) first.
|
||||
// If not present, fail with a 400 Bad Request.
|
||||
if (!stun_msg->GetByteString(STUN_ATTR_USERNAME) ||
|
||||
!stun_msg->GetByteString(STUN_ATTR_MESSAGE_INTEGRITY)) {
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type())
|
||||
<< " without username/M-I from: "
|
||||
<< addr.ToSensitiveString();
|
||||
SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_BAD_REQUEST,
|
||||
STUN_ERROR_REASON_BAD_REQUEST);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the username is bad or unknown, fail with a 401 Unauthorized.
|
||||
std::string local_ufrag;
|
||||
std::string remote_ufrag;
|
||||
if (!ParseStunUsername(stun_msg.get(), &local_ufrag, &remote_ufrag) ||
|
||||
local_ufrag != username_fragment()) {
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type())
|
||||
<< " with bad local username " << local_ufrag
|
||||
<< " from " << addr.ToSensitiveString();
|
||||
SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_UNAUTHORIZED,
|
||||
STUN_ERROR_REASON_UNAUTHORIZED);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If ICE, and the MESSAGE-INTEGRITY is bad, fail with a 401 Unauthorized
|
||||
if (stun_msg->ValidateMessageIntegrity(password_) !=
|
||||
StunMessage::IntegrityStatus::kIntegrityOk) {
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type())
|
||||
<< " with bad M-I from " << addr.ToSensitiveString()
|
||||
<< ", password_=" << password_;
|
||||
SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_UNAUTHORIZED,
|
||||
STUN_ERROR_REASON_UNAUTHORIZED);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If a request contains unknown comprehension-required attributes, reply
|
||||
// with an error. See RFC5389 section 7.3.1.
|
||||
if (!unknown_attributes.empty()) {
|
||||
SendUnknownAttributesErrorResponse(stun_msg.get(), addr,
|
||||
unknown_attributes);
|
||||
return true;
|
||||
}
|
||||
|
||||
out_username->assign(remote_ufrag);
|
||||
} else if ((stun_msg->type() == STUN_BINDING_RESPONSE) ||
|
||||
(stun_msg->type() == STUN_BINDING_ERROR_RESPONSE)) {
|
||||
if (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE) {
|
||||
if (const StunErrorCodeAttribute* error_code = stun_msg->GetErrorCode()) {
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type())
|
||||
<< ": class=" << error_code->eclass()
|
||||
<< " number=" << error_code->number() << " reason='"
|
||||
<< error_code->reason() << "' from "
|
||||
<< addr.ToSensitiveString();
|
||||
// Return message to allow error-specific processing
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type())
|
||||
<< " without a error code from "
|
||||
<< addr.ToSensitiveString();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// If a response contains unknown comprehension-required attributes, it's
|
||||
// simply discarded and the transaction is considered failed. See RFC5389
|
||||
// sections 7.3.3 and 7.3.4.
|
||||
if (!unknown_attributes.empty()) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Discarding STUN response due to unknown "
|
||||
"comprehension-required attribute";
|
||||
return true;
|
||||
}
|
||||
// NOTE: Username should not be used in verifying response messages.
|
||||
out_username->clear();
|
||||
} else if (stun_msg->type() == STUN_BINDING_INDICATION) {
|
||||
RTC_LOG(LS_VERBOSE) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type()) << ": from "
|
||||
<< addr.ToSensitiveString();
|
||||
out_username->clear();
|
||||
|
||||
// If an indication contains unknown comprehension-required attributes,[]
|
||||
// it's simply discarded. See RFC5389 section 7.3.2.
|
||||
if (!unknown_attributes.empty()) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Discarding STUN indication due to "
|
||||
"unknown comprehension-required attribute";
|
||||
return true;
|
||||
}
|
||||
// No stun attributes will be verified, if it's stun indication message.
|
||||
// Returning from end of the this method.
|
||||
} else if (stun_msg->type() == GOOG_PING_REQUEST) {
|
||||
if (stun_msg->ValidateMessageIntegrity(password_) !=
|
||||
StunMessage::IntegrityStatus::kIntegrityOk) {
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type())
|
||||
<< " with bad M-I from " << addr.ToSensitiveString()
|
||||
<< ", password_=" << password_;
|
||||
SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_UNAUTHORIZED,
|
||||
STUN_ERROR_REASON_UNAUTHORIZED);
|
||||
return true;
|
||||
}
|
||||
RTC_LOG(LS_VERBOSE) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type()) << " from "
|
||||
<< addr.ToSensitiveString();
|
||||
out_username->clear();
|
||||
} else if (stun_msg->type() == GOOG_PING_RESPONSE ||
|
||||
stun_msg->type() == GOOG_PING_ERROR_RESPONSE) {
|
||||
// note: the MessageIntegrity32 will be verified in Connection.cc
|
||||
RTC_LOG(LS_VERBOSE) << ToString() << ": Received "
|
||||
<< StunMethodToString(stun_msg->type()) << " from "
|
||||
<< addr.ToSensitiveString();
|
||||
out_username->clear();
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Received STUN packet with invalid type ("
|
||||
<< stun_msg->type() << ") from "
|
||||
<< addr.ToSensitiveString();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return the STUN message found.
|
||||
*out_msg = std::move(stun_msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Port::IsCompatibleAddress(const rtc::SocketAddress& addr) {
|
||||
// Get a representative IP for the Network this port is configured to use.
|
||||
rtc::IPAddress ip = network_->GetBestIP();
|
||||
// We use single-stack sockets, so families must match.
|
||||
if (addr.family() != ip.family()) {
|
||||
return false;
|
||||
}
|
||||
// Link-local IPv6 ports can only connect to other link-local IPv6 ports.
|
||||
if (ip.family() == AF_INET6 &&
|
||||
(IPIsLinkLocal(ip) != IPIsLinkLocal(addr.ipaddr()))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
rtc::DiffServCodePoint Port::StunDscpValue() const {
|
||||
// By default, inherit from whatever the MediaChannel sends.
|
||||
return rtc::DSCP_NO_CHANGE;
|
||||
}
|
||||
|
||||
void Port::DestroyAllConnections() {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
for (auto& [unused, connection] : connections_) {
|
||||
connection->Shutdown();
|
||||
delete connection;
|
||||
}
|
||||
connections_.clear();
|
||||
}
|
||||
|
||||
void Port::set_timeout_delay(int delay) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
// Although this method is meant to only be used by tests, some downstream
|
||||
// projects have started using it. Ideally we should update our tests to not
|
||||
// require to modify this state and instead use a testing harness that allows
|
||||
// adjusting the clock and then just use the kPortTimeoutDelay constant
|
||||
// directly.
|
||||
timeout_delay_ = delay;
|
||||
}
|
||||
|
||||
bool Port::ParseStunUsername(const StunMessage* stun_msg,
|
||||
std::string* local_ufrag,
|
||||
std::string* remote_ufrag) const {
|
||||
// The packet must include a username that either begins or ends with our
|
||||
// fragment. It should begin with our fragment if it is a request and it
|
||||
// should end with our fragment if it is a response.
|
||||
local_ufrag->clear();
|
||||
remote_ufrag->clear();
|
||||
const StunByteStringAttribute* username_attr =
|
||||
stun_msg->GetByteString(STUN_ATTR_USERNAME);
|
||||
if (username_attr == NULL)
|
||||
return false;
|
||||
|
||||
// RFRAG:LFRAG
|
||||
const absl::string_view username = username_attr->string_view();
|
||||
size_t colon_pos = username.find(':');
|
||||
if (colon_pos == absl::string_view::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*local_ufrag = std::string(username.substr(0, colon_pos));
|
||||
*remote_ufrag = std::string(username.substr(colon_pos + 1, username.size()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Port::MaybeIceRoleConflict(const rtc::SocketAddress& addr,
|
||||
IceMessage* stun_msg,
|
||||
absl::string_view remote_ufrag) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
// Validate ICE_CONTROLLING or ICE_CONTROLLED attributes.
|
||||
bool ret = true;
|
||||
IceRole remote_ice_role = ICEROLE_UNKNOWN;
|
||||
uint64_t remote_tiebreaker = 0;
|
||||
const StunUInt64Attribute* stun_attr =
|
||||
stun_msg->GetUInt64(STUN_ATTR_ICE_CONTROLLING);
|
||||
if (stun_attr) {
|
||||
remote_ice_role = ICEROLE_CONTROLLING;
|
||||
remote_tiebreaker = stun_attr->value();
|
||||
}
|
||||
|
||||
// If `remote_ufrag` is same as port local username fragment and
|
||||
// tie breaker value received in the ping message matches port
|
||||
// tiebreaker value this must be a loopback call.
|
||||
// We will treat this as valid scenario.
|
||||
if (remote_ice_role == ICEROLE_CONTROLLING &&
|
||||
username_fragment() == remote_ufrag &&
|
||||
remote_tiebreaker == IceTiebreaker()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
stun_attr = stun_msg->GetUInt64(STUN_ATTR_ICE_CONTROLLED);
|
||||
if (stun_attr) {
|
||||
remote_ice_role = ICEROLE_CONTROLLED;
|
||||
remote_tiebreaker = stun_attr->value();
|
||||
}
|
||||
|
||||
switch (ice_role_) {
|
||||
case ICEROLE_CONTROLLING:
|
||||
if (ICEROLE_CONTROLLING == remote_ice_role) {
|
||||
if (remote_tiebreaker >= tiebreaker_) {
|
||||
SignalRoleConflict(this);
|
||||
} else {
|
||||
// Send Role Conflict (487) error response.
|
||||
SendBindingErrorResponse(stun_msg, addr, STUN_ERROR_ROLE_CONFLICT,
|
||||
STUN_ERROR_REASON_ROLE_CONFLICT);
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ICEROLE_CONTROLLED:
|
||||
if (ICEROLE_CONTROLLED == remote_ice_role) {
|
||||
if (remote_tiebreaker < tiebreaker_) {
|
||||
SignalRoleConflict(this);
|
||||
} else {
|
||||
// Send Role Conflict (487) error response.
|
||||
SendBindingErrorResponse(stun_msg, addr, STUN_ERROR_ROLE_CONFLICT,
|
||||
STUN_ERROR_REASON_ROLE_CONFLICT);
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string Port::CreateStunUsername(absl::string_view remote_username) const {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
return std::string(remote_username) + ":" + username_fragment();
|
||||
}
|
||||
|
||||
bool Port::HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet) {
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Port::CanHandleIncomingPacketsFrom(const rtc::SocketAddress&) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Port::SendBindingErrorResponse(StunMessage* message,
|
||||
const rtc::SocketAddress& addr,
|
||||
int error_code,
|
||||
absl::string_view reason) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
RTC_DCHECK(message->type() == STUN_BINDING_REQUEST ||
|
||||
message->type() == GOOG_PING_REQUEST);
|
||||
|
||||
// Fill in the response message.
|
||||
StunMessage response(message->type() == STUN_BINDING_REQUEST
|
||||
? STUN_BINDING_ERROR_RESPONSE
|
||||
: GOOG_PING_ERROR_RESPONSE,
|
||||
message->transaction_id());
|
||||
|
||||
// When doing GICE, we need to write out the error code incorrectly to
|
||||
// maintain backwards compatiblility.
|
||||
auto error_attr = StunAttribute::CreateErrorCode();
|
||||
error_attr->SetCode(error_code);
|
||||
error_attr->SetReason(std::string(reason));
|
||||
response.AddAttribute(std::move(error_attr));
|
||||
|
||||
// Per Section 10.1.2, certain error cases don't get a MESSAGE-INTEGRITY,
|
||||
// because we don't have enough information to determine the shared secret.
|
||||
if (error_code != STUN_ERROR_BAD_REQUEST &&
|
||||
error_code != STUN_ERROR_UNAUTHORIZED &&
|
||||
message->type() != GOOG_PING_REQUEST) {
|
||||
if (message->type() == STUN_BINDING_REQUEST) {
|
||||
response.AddMessageIntegrity(password_);
|
||||
} else {
|
||||
response.AddMessageIntegrity32(password_);
|
||||
}
|
||||
}
|
||||
|
||||
if (message->type() == STUN_BINDING_REQUEST) {
|
||||
response.AddFingerprint();
|
||||
}
|
||||
|
||||
// Send the response message.
|
||||
rtc::ByteBufferWriter buf;
|
||||
response.Write(&buf);
|
||||
rtc::PacketOptions options(StunDscpValue());
|
||||
options.info_signaled_after_sent.packet_type =
|
||||
rtc::PacketType::kIceConnectivityCheckResponse;
|
||||
SendTo(buf.Data(), buf.Length(), addr, options, false);
|
||||
RTC_LOG(LS_INFO) << ToString() << ": Sending STUN "
|
||||
<< StunMethodToString(response.type())
|
||||
<< ": reason=" << reason << " to "
|
||||
<< addr.ToSensitiveString();
|
||||
}
|
||||
|
||||
void Port::SendUnknownAttributesErrorResponse(
|
||||
StunMessage* message,
|
||||
const rtc::SocketAddress& addr,
|
||||
const std::vector<uint16_t>& unknown_types) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
RTC_DCHECK(message->type() == STUN_BINDING_REQUEST);
|
||||
|
||||
// Fill in the response message.
|
||||
StunMessage response(STUN_BINDING_ERROR_RESPONSE, message->transaction_id());
|
||||
|
||||
auto error_attr = StunAttribute::CreateErrorCode();
|
||||
error_attr->SetCode(STUN_ERROR_UNKNOWN_ATTRIBUTE);
|
||||
error_attr->SetReason(STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE);
|
||||
response.AddAttribute(std::move(error_attr));
|
||||
|
||||
std::unique_ptr<StunUInt16ListAttribute> unknown_attr =
|
||||
StunAttribute::CreateUnknownAttributes();
|
||||
for (uint16_t type : unknown_types) {
|
||||
unknown_attr->AddType(type);
|
||||
}
|
||||
response.AddAttribute(std::move(unknown_attr));
|
||||
|
||||
response.AddMessageIntegrity(password_);
|
||||
response.AddFingerprint();
|
||||
|
||||
// Send the response message.
|
||||
rtc::ByteBufferWriter buf;
|
||||
response.Write(&buf);
|
||||
rtc::PacketOptions options(StunDscpValue());
|
||||
options.info_signaled_after_sent.packet_type =
|
||||
rtc::PacketType::kIceConnectivityCheckResponse;
|
||||
SendTo(buf.Data(), buf.Length(), addr, options, false);
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Sending STUN binding error: reason="
|
||||
<< STUN_ERROR_UNKNOWN_ATTRIBUTE << " to "
|
||||
<< addr.ToSensitiveString();
|
||||
}
|
||||
|
||||
void Port::KeepAliveUntilPruned() {
|
||||
// If it is pruned, we won't bring it up again.
|
||||
if (state_ == State::INIT) {
|
||||
state_ = State::KEEP_ALIVE_UNTIL_PRUNED;
|
||||
}
|
||||
}
|
||||
|
||||
void Port::Prune() {
|
||||
state_ = State::PRUNED;
|
||||
PostDestroyIfDead(/*delayed=*/false);
|
||||
}
|
||||
|
||||
// Call to stop any currently pending operations from running.
|
||||
void Port::CancelPendingTasks() {
|
||||
TRACE_EVENT0("webrtc", "Port::CancelPendingTasks");
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
weak_factory_.InvalidateWeakPtrs();
|
||||
}
|
||||
|
||||
void Port::PostDestroyIfDead(bool delayed) {
|
||||
rtc::WeakPtr<Port> weak_ptr = NewWeakPtr();
|
||||
auto task = [weak_ptr = std::move(weak_ptr)] {
|
||||
if (weak_ptr) {
|
||||
weak_ptr->DestroyIfDead();
|
||||
}
|
||||
};
|
||||
if (delayed) {
|
||||
thread_->PostDelayedTask(std::move(task),
|
||||
TimeDelta::Millis(timeout_delay_));
|
||||
} else {
|
||||
thread_->PostTask(std::move(task));
|
||||
}
|
||||
}
|
||||
|
||||
void Port::DestroyIfDead() {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
bool dead =
|
||||
(state_ == State::INIT || state_ == State::PRUNED) &&
|
||||
connections_.empty() &&
|
||||
rtc::TimeMillis() - last_time_all_connections_removed_ >= timeout_delay_;
|
||||
if (dead) {
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void Port::SubscribePortDestroyed(
|
||||
std::function<void(PortInterface*)> callback) {
|
||||
port_destroyed_callback_list_.AddReceiver(callback);
|
||||
}
|
||||
|
||||
void Port::SendPortDestroyed(Port* port) {
|
||||
port_destroyed_callback_list_.Send(port);
|
||||
}
|
||||
void Port::OnNetworkTypeChanged(const rtc::Network* network) {
|
||||
RTC_DCHECK(network == network_);
|
||||
|
||||
UpdateNetworkCost();
|
||||
}
|
||||
|
||||
std::string Port::ToString() const {
|
||||
rtc::StringBuilder ss;
|
||||
ss << "Port[" << rtc::ToHex(reinterpret_cast<uintptr_t>(this)) << ":"
|
||||
<< content_name_ << ":" << component_ << ":" << generation_ << ":" << type_
|
||||
<< ":" << network_->ToString() << "]";
|
||||
return ss.Release();
|
||||
}
|
||||
|
||||
// TODO(honghaiz): Make the network cost configurable from user setting.
|
||||
void Port::UpdateNetworkCost() {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
uint16_t new_cost = network_->GetCost(field_trials());
|
||||
if (network_cost_ == new_cost) {
|
||||
return;
|
||||
}
|
||||
RTC_LOG(LS_INFO) << "Network cost changed from " << network_cost_ << " to "
|
||||
<< new_cost
|
||||
<< ". Number of candidates created: " << candidates_.size()
|
||||
<< ". Number of connections created: "
|
||||
<< connections_.size();
|
||||
network_cost_ = new_cost;
|
||||
for (cricket::Candidate& candidate : candidates_)
|
||||
candidate.set_network_cost(network_cost_);
|
||||
|
||||
for (auto& [unused, connection] : connections_)
|
||||
connection->SetLocalCandidateNetworkCost(network_cost_);
|
||||
}
|
||||
|
||||
void Port::EnablePortPackets() {
|
||||
enable_port_packets_ = true;
|
||||
}
|
||||
|
||||
bool Port::OnConnectionDestroyed(Connection* conn) {
|
||||
if (connections_.erase(conn->remote_candidate().address()) == 0) {
|
||||
// This could indicate a programmer error outside of webrtc so while we
|
||||
// do have this check here to alert external developers, we also need to
|
||||
// handle it since it might be a corner case not caught in tests.
|
||||
RTC_DCHECK_NOTREACHED() << "Calling Destroy recursively?";
|
||||
return false;
|
||||
}
|
||||
|
||||
HandleConnectionDestroyed(conn);
|
||||
|
||||
// Ports time out after all connections fail if it is not marked as
|
||||
// "keep alive until pruned."
|
||||
// Note: If a new connection is added after this message is posted, but it
|
||||
// fails and is removed before kPortTimeoutDelay, then this message will
|
||||
// not cause the Port to be destroyed.
|
||||
if (connections_.empty()) {
|
||||
last_time_all_connections_removed_ = rtc::TimeMillis();
|
||||
PostDestroyIfDead(/*delayed=*/true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Port::DestroyConnectionInternal(Connection* conn, bool async) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
if (!OnConnectionDestroyed(conn))
|
||||
return;
|
||||
|
||||
conn->Shutdown();
|
||||
if (async) {
|
||||
// Unwind the stack before deleting the object in case upstream callers
|
||||
// need to refer to the Connection's state as part of teardown.
|
||||
// NOTE: We move ownership of `conn` into the capture section of the lambda
|
||||
// so that the object will always be deleted, including if PostTask fails.
|
||||
// In such a case (only tests), deletion would happen inside of the call
|
||||
// to `DestroyConnection()`.
|
||||
thread_->PostTask([conn = absl::WrapUnique(conn)]() {});
|
||||
} else {
|
||||
delete conn;
|
||||
}
|
||||
}
|
||||
|
||||
void Port::Destroy() {
|
||||
RTC_DCHECK(connections_.empty());
|
||||
RTC_LOG(LS_INFO) << ToString() << ": Port deleted";
|
||||
SendPortDestroyed(this);
|
||||
delete this;
|
||||
}
|
||||
|
||||
const std::string& Port::username_fragment() const {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
return ice_username_fragment_;
|
||||
}
|
||||
|
||||
void Port::CopyPortInformationToPacketInfo(rtc::PacketInfo* info) const {
|
||||
info->protocol = ConvertProtocolTypeToPacketInfoProtocolType(GetProtocol());
|
||||
info->network_id = Network()->id();
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
536
TMessagesProj/jni/voip/webrtc/p2p/base/port.h
Normal file
536
TMessagesProj/jni/voip/webrtc/p2p/base/port.h
Normal file
|
|
@ -0,0 +1,536 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_PORT_H_
|
||||
#define P2P_BASE_PORT_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/candidate.h"
|
||||
#include "api/field_trials_view.h"
|
||||
#include "api/packet_socket_factory.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "api/transport/field_trial_based_config.h"
|
||||
#include "api/transport/stun.h"
|
||||
#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h"
|
||||
#include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h"
|
||||
#include "logging/rtc_event_log/ice_logger.h"
|
||||
#include "p2p/base/candidate_pair_interface.h"
|
||||
#include "p2p/base/connection.h"
|
||||
#include "p2p/base/connection_info.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "p2p/base/port_interface.h"
|
||||
#include "p2p/base/stun_request.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/callback_list.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/dscp.h"
|
||||
#include "rtc_base/memory/always_valid_pointer.h"
|
||||
#include "rtc_base/net_helper.h"
|
||||
#include "rtc_base/network.h"
|
||||
#include "rtc_base/network/received_packet.h"
|
||||
#include "rtc_base/network/sent_packet.h"
|
||||
#include "rtc_base/proxy_info.h"
|
||||
#include "rtc_base/rate_tracker.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
#include "rtc_base/weak_ptr.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// RFC 6544, TCP candidate encoding rules.
|
||||
extern const int DISCARD_PORT;
|
||||
extern const char TCPTYPE_ACTIVE_STR[];
|
||||
extern const char TCPTYPE_PASSIVE_STR[];
|
||||
extern const char TCPTYPE_SIMOPEN_STR[];
|
||||
|
||||
enum class MdnsNameRegistrationStatus {
|
||||
// IP concealment with mDNS is not enabled or the name registration process is
|
||||
// not started yet.
|
||||
kNotStarted,
|
||||
// A request to create and register an mDNS name for a local IP address of a
|
||||
// host candidate is sent to the mDNS responder.
|
||||
kInProgress,
|
||||
// The name registration is complete and the created name is returned by the
|
||||
// mDNS responder.
|
||||
kCompleted,
|
||||
};
|
||||
|
||||
// Stats that we can return about the port of a STUN candidate.
|
||||
class StunStats {
|
||||
public:
|
||||
StunStats() = default;
|
||||
StunStats(const StunStats&) = default;
|
||||
~StunStats() = default;
|
||||
|
||||
StunStats& operator=(const StunStats& other) = default;
|
||||
|
||||
int stun_binding_requests_sent = 0;
|
||||
int stun_binding_responses_received = 0;
|
||||
double stun_binding_rtt_ms_total = 0;
|
||||
double stun_binding_rtt_ms_squared_total = 0;
|
||||
};
|
||||
|
||||
// Stats that we can return about a candidate.
|
||||
class CandidateStats {
|
||||
public:
|
||||
CandidateStats() = default;
|
||||
CandidateStats(const CandidateStats&) = default;
|
||||
CandidateStats(CandidateStats&&) = default;
|
||||
CandidateStats(Candidate candidate,
|
||||
absl::optional<StunStats> stats = absl::nullopt)
|
||||
: candidate_(std::move(candidate)), stun_stats_(std::move(stats)) {}
|
||||
~CandidateStats() = default;
|
||||
|
||||
CandidateStats& operator=(const CandidateStats& other) = default;
|
||||
|
||||
const Candidate& candidate() const { return candidate_; }
|
||||
|
||||
const absl::optional<StunStats>& stun_stats() const { return stun_stats_; }
|
||||
|
||||
private:
|
||||
Candidate candidate_;
|
||||
// STUN port stats if this candidate is a STUN candidate.
|
||||
absl::optional<StunStats> stun_stats_;
|
||||
};
|
||||
|
||||
typedef std::vector<CandidateStats> CandidateStatsList;
|
||||
|
||||
const char* ProtoToString(ProtocolType proto);
|
||||
absl::optional<ProtocolType> StringToProto(absl::string_view proto_name);
|
||||
|
||||
struct ProtocolAddress {
|
||||
rtc::SocketAddress address;
|
||||
ProtocolType proto;
|
||||
|
||||
ProtocolAddress(const rtc::SocketAddress& a, ProtocolType p)
|
||||
: address(a), proto(p) {}
|
||||
|
||||
bool operator==(const ProtocolAddress& o) const {
|
||||
return address == o.address && proto == o.proto;
|
||||
}
|
||||
bool operator!=(const ProtocolAddress& o) const { return !(*this == o); }
|
||||
};
|
||||
|
||||
struct IceCandidateErrorEvent {
|
||||
IceCandidateErrorEvent() = default;
|
||||
IceCandidateErrorEvent(absl::string_view address,
|
||||
int port,
|
||||
absl::string_view url,
|
||||
int error_code,
|
||||
absl::string_view error_text)
|
||||
: address(std::move(address)),
|
||||
port(port),
|
||||
url(std::move(url)),
|
||||
error_code(error_code),
|
||||
error_text(std::move(error_text)) {}
|
||||
|
||||
std::string address;
|
||||
int port = 0;
|
||||
std::string url;
|
||||
int error_code = 0;
|
||||
std::string error_text;
|
||||
};
|
||||
|
||||
struct CandidatePairChangeEvent {
|
||||
CandidatePair selected_candidate_pair;
|
||||
int64_t last_data_received_ms;
|
||||
std::string reason;
|
||||
// How long do we estimate that we've been disconnected.
|
||||
int64_t estimated_disconnected_time_ms;
|
||||
};
|
||||
|
||||
typedef std::set<rtc::SocketAddress> ServerAddresses;
|
||||
|
||||
// Represents a local communication mechanism that can be used to create
|
||||
// connections to similar mechanisms of the other client. Subclasses of this
|
||||
// one add support for specific mechanisms like local UDP ports.
|
||||
class RTC_EXPORT Port : public PortInterface, public sigslot::has_slots<> {
|
||||
public:
|
||||
// INIT: The state when a port is just created.
|
||||
// KEEP_ALIVE_UNTIL_PRUNED: A port should not be destroyed even if no
|
||||
// connection is using it.
|
||||
// PRUNED: It will be destroyed if no connection is using it for a period of
|
||||
// 30 seconds.
|
||||
enum class State { INIT, KEEP_ALIVE_UNTIL_PRUNED, PRUNED };
|
||||
Port(webrtc::TaskQueueBase* thread,
|
||||
absl::string_view type ABSL_ATTRIBUTE_LIFETIME_BOUND,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
absl::string_view username_fragment,
|
||||
absl::string_view password,
|
||||
const webrtc::FieldTrialsView* field_trials = nullptr);
|
||||
Port(webrtc::TaskQueueBase* thread,
|
||||
absl::string_view type ABSL_ATTRIBUTE_LIFETIME_BOUND,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
absl::string_view username_fragment,
|
||||
absl::string_view password,
|
||||
const webrtc::FieldTrialsView* field_trials = nullptr);
|
||||
~Port() override;
|
||||
|
||||
// Note that the port type does NOT uniquely identify different subclasses of
|
||||
// Port. Use the 2-tuple of the port type AND the protocol (GetProtocol()) to
|
||||
// uniquely identify subclasses. Whenever a new subclass of Port introduces a
|
||||
// conflict in the value of the 2-tuple, make sure that the implementation
|
||||
// that relies on this 2-tuple for RTTI is properly changed.
|
||||
const absl::string_view Type() const override;
|
||||
const rtc::Network* Network() const override;
|
||||
|
||||
// Methods to set/get ICE role and tiebreaker values.
|
||||
IceRole GetIceRole() const override;
|
||||
void SetIceRole(IceRole role) override;
|
||||
|
||||
void SetIceTiebreaker(uint64_t tiebreaker) override;
|
||||
uint64_t IceTiebreaker() const override;
|
||||
|
||||
bool SharedSocket() const override;
|
||||
void ResetSharedSocket() { shared_socket_ = false; }
|
||||
|
||||
// Should not destroy the port even if no connection is using it. Called when
|
||||
// a port is ready to use.
|
||||
void KeepAliveUntilPruned();
|
||||
// Allows a port to be destroyed if no connection is using it.
|
||||
void Prune();
|
||||
|
||||
// Call to stop any currently pending operations from running.
|
||||
void CancelPendingTasks();
|
||||
|
||||
// The thread on which this port performs its I/O.
|
||||
webrtc::TaskQueueBase* thread() override { return thread_; }
|
||||
|
||||
// The factory used to create the sockets of this port.
|
||||
rtc::PacketSocketFactory* socket_factory() const override { return factory_; }
|
||||
|
||||
// For debugging purposes.
|
||||
const std::string& content_name() const override { return content_name_; }
|
||||
void set_content_name(absl::string_view content_name) {
|
||||
content_name_ = std::string(content_name);
|
||||
}
|
||||
|
||||
int component() const { return component_; }
|
||||
void set_component(int component) { component_ = component; }
|
||||
|
||||
bool send_retransmit_count_attribute() const override {
|
||||
return send_retransmit_count_attribute_;
|
||||
}
|
||||
void set_send_retransmit_count_attribute(bool enable) {
|
||||
send_retransmit_count_attribute_ = enable;
|
||||
}
|
||||
|
||||
// Identifies the generation that this port was created in.
|
||||
uint32_t generation() const override { return generation_; }
|
||||
void set_generation(uint32_t generation) override {
|
||||
generation_ = generation;
|
||||
}
|
||||
|
||||
const std::string& username_fragment() const;
|
||||
const std::string& password() const { return password_; }
|
||||
|
||||
// May be called when this port was initially created by a pooled
|
||||
// PortAllocatorSession, and is now being assigned to an ICE transport.
|
||||
// Updates the information for candidates as well.
|
||||
void SetIceParameters(int component,
|
||||
absl::string_view username_fragment,
|
||||
absl::string_view password);
|
||||
|
||||
// Fired when candidates are discovered by the port. When all candidates
|
||||
// are discovered that belong to port SignalAddressReady is fired.
|
||||
sigslot::signal2<Port*, const Candidate&> SignalCandidateReady;
|
||||
// Provides all of the above information in one handy object.
|
||||
const std::vector<Candidate>& Candidates() const override;
|
||||
// Fired when candidate discovery failed using certain server.
|
||||
sigslot::signal2<Port*, const IceCandidateErrorEvent&> SignalCandidateError;
|
||||
|
||||
// SignalPortComplete is sent when port completes the task of candidates
|
||||
// allocation.
|
||||
sigslot::signal1<Port*> SignalPortComplete;
|
||||
// This signal sent when port fails to allocate candidates and this port
|
||||
// can't be used in establishing the connections. When port is in shared mode
|
||||
// and port fails to allocate one of the candidates, port shouldn't send
|
||||
// this signal as other candidates might be usefull in establishing the
|
||||
// connection.
|
||||
sigslot::signal1<Port*> SignalPortError;
|
||||
|
||||
void SubscribePortDestroyed(
|
||||
std::function<void(PortInterface*)> callback) override;
|
||||
void SendPortDestroyed(Port* port);
|
||||
// Returns a map containing all of the connections of this port, keyed by the
|
||||
// remote address.
|
||||
typedef std::map<rtc::SocketAddress, Connection*> AddressMap;
|
||||
const AddressMap& connections() { return connections_; }
|
||||
|
||||
// Returns the connection to the given address or NULL if none exists.
|
||||
Connection* GetConnection(const rtc::SocketAddress& remote_addr) override;
|
||||
|
||||
// Removes and deletes a connection object. `DestroyConnection` will
|
||||
// delete the connection object directly whereas `DestroyConnectionAsync`
|
||||
// defers the `delete` operation to when the call stack has been unwound.
|
||||
// Async may be needed when deleting a connection object from within a
|
||||
// callback.
|
||||
void DestroyConnection(Connection* conn) override {
|
||||
DestroyConnectionInternal(conn, false);
|
||||
}
|
||||
|
||||
void DestroyConnectionAsync(Connection* conn) override {
|
||||
DestroyConnectionInternal(conn, true);
|
||||
}
|
||||
|
||||
// In a shared socket mode each port which shares the socket will decide
|
||||
// to accept the packet based on the `remote_addr`. Currently only UDP
|
||||
// port implemented this method.
|
||||
// TODO(mallinath) - Make it pure virtual.
|
||||
virtual bool HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet);
|
||||
|
||||
// Shall the port handle packet from this `remote_addr`.
|
||||
// This method is overridden by TurnPort.
|
||||
virtual bool CanHandleIncomingPacketsFrom(
|
||||
const rtc::SocketAddress& remote_addr) const;
|
||||
|
||||
// Sends a response error to the given request.
|
||||
void SendBindingErrorResponse(StunMessage* message,
|
||||
const rtc::SocketAddress& addr,
|
||||
int error_code,
|
||||
absl::string_view reason) override;
|
||||
void SendUnknownAttributesErrorResponse(
|
||||
StunMessage* message,
|
||||
const rtc::SocketAddress& addr,
|
||||
const std::vector<uint16_t>& unknown_types);
|
||||
|
||||
void set_proxy(absl::string_view user_agent, const rtc::ProxyInfo& proxy) {
|
||||
user_agent_ = std::string(user_agent);
|
||||
proxy_ = proxy;
|
||||
}
|
||||
const std::string& user_agent() override { return user_agent_; }
|
||||
const rtc::ProxyInfo& proxy() override { return proxy_; }
|
||||
|
||||
void EnablePortPackets() override;
|
||||
|
||||
// Called if the port has no connections and is no longer useful.
|
||||
void Destroy();
|
||||
|
||||
// Debugging description of this port
|
||||
std::string ToString() const override;
|
||||
uint16_t min_port() { return min_port_; }
|
||||
uint16_t max_port() { return max_port_; }
|
||||
|
||||
// Timeout shortening function to speed up unit tests.
|
||||
void set_timeout_delay(int delay);
|
||||
|
||||
// This method will return local and remote username fragements from the
|
||||
// stun username attribute if present.
|
||||
bool ParseStunUsername(const StunMessage* stun_msg,
|
||||
std::string* local_username,
|
||||
std::string* remote_username) const override;
|
||||
std::string CreateStunUsername(
|
||||
absl::string_view remote_username) const override;
|
||||
|
||||
bool MaybeIceRoleConflict(const rtc::SocketAddress& addr,
|
||||
IceMessage* stun_msg,
|
||||
absl::string_view remote_ufrag) override;
|
||||
|
||||
// Called when a packet has been sent to the socket.
|
||||
// This is made pure virtual to notify subclasses of Port that they MUST
|
||||
// listen to AsyncPacketSocket::SignalSentPacket and then call
|
||||
// PortInterface::OnSentPacket.
|
||||
virtual void OnSentPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::SentPacket& sent_packet) = 0;
|
||||
|
||||
// Called when the socket is currently able to send.
|
||||
void OnReadyToSend();
|
||||
|
||||
// Called when the Connection discovers a local peer reflexive candidate.
|
||||
void AddPrflxCandidate(const Candidate& local) override;
|
||||
|
||||
int16_t network_cost() const override { return network_cost_; }
|
||||
|
||||
void GetStunStats(absl::optional<StunStats>* stats) override {}
|
||||
|
||||
// Foundation: An arbitrary string that is the same for two candidates
|
||||
// that have the same type, base IP address, protocol (UDP, TCP,
|
||||
// etc.), and STUN or TURN server. If any of these are different,
|
||||
// then the foundation will be different. Two candidate pairs with
|
||||
// the same foundation pairs are likely to have similar network
|
||||
// characteristics. Foundations are used in the frozen algorithm.
|
||||
std::string ComputeFoundation(
|
||||
absl::string_view type,
|
||||
absl::string_view protocol,
|
||||
absl::string_view relay_protocol,
|
||||
const rtc::SocketAddress& base_address) override;
|
||||
|
||||
protected:
|
||||
void UpdateNetworkCost() override;
|
||||
|
||||
rtc::WeakPtr<Port> NewWeakPtr() { return weak_factory_.GetWeakPtr(); }
|
||||
|
||||
void AddAddress(const rtc::SocketAddress& address,
|
||||
const rtc::SocketAddress& base_address,
|
||||
const rtc::SocketAddress& related_address,
|
||||
absl::string_view protocol,
|
||||
absl::string_view relay_protocol,
|
||||
absl::string_view tcptype,
|
||||
absl::string_view type,
|
||||
uint32_t type_preference,
|
||||
uint32_t relay_preference,
|
||||
absl::string_view url,
|
||||
bool is_final);
|
||||
|
||||
void FinishAddingAddress(const Candidate& c, bool is_final)
|
||||
RTC_RUN_ON(thread_);
|
||||
|
||||
virtual void PostAddAddress(bool is_final);
|
||||
|
||||
// Adds the given connection to the map keyed by the remote candidate address.
|
||||
// If an existing connection has the same address, the existing one will be
|
||||
// replaced and destroyed.
|
||||
void AddOrReplaceConnection(Connection* conn);
|
||||
|
||||
// Called when a packet is received from an unknown address that is not
|
||||
// currently a connection. If this is an authenticated STUN binding request,
|
||||
// then we will signal the client.
|
||||
void OnReadPacket(const rtc::ReceivedPacket& packet, ProtocolType proto);
|
||||
|
||||
[[deprecated(
|
||||
"Use OnReadPacket(const rtc::ReceivedPacket& packet, ProtocolType "
|
||||
"proto)")]] void
|
||||
OnReadPacket(const char* data,
|
||||
size_t size,
|
||||
const rtc::SocketAddress& addr,
|
||||
ProtocolType proto) {
|
||||
OnReadPacket(rtc::ReceivedPacket::CreateFromLegacy(
|
||||
data, size, /*packet_time_us = */ -1, addr),
|
||||
proto);
|
||||
}
|
||||
|
||||
// If the given data comprises a complete and correct STUN message then the
|
||||
// return value is true, otherwise false. If the message username corresponds
|
||||
// with this port's username fragment, msg will contain the parsed STUN
|
||||
// message. Otherwise, the function may send a STUN response internally.
|
||||
// remote_username contains the remote fragment of the STUN username.
|
||||
bool GetStunMessage(const char* data,
|
||||
size_t size,
|
||||
const rtc::SocketAddress& addr,
|
||||
std::unique_ptr<IceMessage>* out_msg,
|
||||
std::string* out_username) override;
|
||||
|
||||
// Checks if the address in addr is compatible with the port's ip.
|
||||
bool IsCompatibleAddress(const rtc::SocketAddress& addr);
|
||||
|
||||
// Returns DSCP value packets generated by the port itself should use.
|
||||
rtc::DiffServCodePoint StunDscpValue() const override;
|
||||
|
||||
// Extra work to be done in subclasses when a connection is destroyed.
|
||||
virtual void HandleConnectionDestroyed(Connection* conn) {}
|
||||
|
||||
void DestroyAllConnections();
|
||||
|
||||
void CopyPortInformationToPacketInfo(rtc::PacketInfo* info) const;
|
||||
|
||||
MdnsNameRegistrationStatus mdns_name_registration_status() const {
|
||||
return mdns_name_registration_status_;
|
||||
}
|
||||
void set_mdns_name_registration_status(MdnsNameRegistrationStatus status) {
|
||||
mdns_name_registration_status_ = status;
|
||||
}
|
||||
|
||||
const webrtc::FieldTrialsView& field_trials() const { return *field_trials_; }
|
||||
|
||||
private:
|
||||
void Construct();
|
||||
|
||||
void PostDestroyIfDead(bool delayed);
|
||||
void DestroyIfDead();
|
||||
|
||||
// Called internally when deleting a connection object.
|
||||
// Returns true if the connection object was removed from the `connections_`
|
||||
// list and the state updated accordingly. If the connection was not found
|
||||
// in the list, the return value is false. Note that this may indicate
|
||||
// incorrect behavior of external code that might be attempting to delete
|
||||
// connection objects from within a 'on destroyed' callback notification
|
||||
// for the connection object itself.
|
||||
bool OnConnectionDestroyed(Connection* conn);
|
||||
|
||||
// Private implementation of DestroyConnection to keep the async usage
|
||||
// distinct.
|
||||
void DestroyConnectionInternal(Connection* conn, bool async);
|
||||
|
||||
void OnNetworkTypeChanged(const rtc::Network* network);
|
||||
|
||||
webrtc::TaskQueueBase* const thread_;
|
||||
rtc::PacketSocketFactory* const factory_;
|
||||
const absl::string_view type_;
|
||||
bool send_retransmit_count_attribute_;
|
||||
const rtc::Network* network_;
|
||||
uint16_t min_port_;
|
||||
uint16_t max_port_;
|
||||
std::string content_name_;
|
||||
int component_;
|
||||
uint32_t generation_;
|
||||
// In order to establish a connection to this Port (so that real data can be
|
||||
// sent through), the other side must send us a STUN binding request that is
|
||||
// authenticated with this username_fragment and password.
|
||||
// PortAllocatorSession will provide these username_fragment and password.
|
||||
std::string ice_username_fragment_ RTC_GUARDED_BY(thread_);
|
||||
std::string password_ RTC_GUARDED_BY(thread_);
|
||||
std::vector<Candidate> candidates_ RTC_GUARDED_BY(thread_);
|
||||
AddressMap connections_;
|
||||
int timeout_delay_;
|
||||
bool enable_port_packets_;
|
||||
IceRole ice_role_;
|
||||
uint64_t tiebreaker_;
|
||||
bool shared_socket_;
|
||||
// Information to use when going through a proxy.
|
||||
std::string user_agent_;
|
||||
rtc::ProxyInfo proxy_;
|
||||
|
||||
// A virtual cost perceived by the user, usually based on the network type
|
||||
// (WiFi. vs. Cellular). It takes precedence over the priority when
|
||||
// comparing two connections.
|
||||
int16_t network_cost_;
|
||||
State state_ = State::INIT;
|
||||
int64_t last_time_all_connections_removed_ = 0;
|
||||
MdnsNameRegistrationStatus mdns_name_registration_status_ =
|
||||
MdnsNameRegistrationStatus::kNotStarted;
|
||||
|
||||
rtc::WeakPtrFactory<Port> weak_factory_;
|
||||
webrtc::AlwaysValidPointer<const webrtc::FieldTrialsView,
|
||||
webrtc::FieldTrialBasedConfig>
|
||||
field_trials_;
|
||||
|
||||
bool MaybeObfuscateAddress(const Candidate& c, bool is_final)
|
||||
RTC_RUN_ON(thread_);
|
||||
|
||||
webrtc::CallbackList<PortInterface*> port_destroyed_callback_list_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_PORT_H_
|
||||
324
TMessagesProj/jni/voip/webrtc/p2p/base/port_allocator.cc
Normal file
324
TMessagesProj/jni/voip/webrtc/p2p/base/port_allocator.cc
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/port_allocator.h"
|
||||
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "p2p/base/ice_credentials_iterator.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
RelayServerConfig::RelayServerConfig() {}
|
||||
|
||||
RelayServerConfig::RelayServerConfig(const rtc::SocketAddress& address,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
ProtocolType proto)
|
||||
: credentials(username, password) {
|
||||
ports.push_back(ProtocolAddress(address, proto));
|
||||
}
|
||||
|
||||
RelayServerConfig::RelayServerConfig(absl::string_view address,
|
||||
int port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
ProtocolType proto)
|
||||
: RelayServerConfig(rtc::SocketAddress(address, port),
|
||||
username,
|
||||
password,
|
||||
proto) {}
|
||||
|
||||
// Legacy constructor where "secure" and PROTO_TCP implies PROTO_TLS.
|
||||
RelayServerConfig::RelayServerConfig(absl::string_view address,
|
||||
int port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
ProtocolType proto,
|
||||
bool secure)
|
||||
: RelayServerConfig(address,
|
||||
port,
|
||||
username,
|
||||
password,
|
||||
(proto == PROTO_TCP && secure ? PROTO_TLS : proto)) {}
|
||||
|
||||
RelayServerConfig::RelayServerConfig(const RelayServerConfig&) = default;
|
||||
|
||||
RelayServerConfig::~RelayServerConfig() = default;
|
||||
|
||||
PortAllocatorSession::PortAllocatorSession(absl::string_view content_name,
|
||||
int component,
|
||||
absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd,
|
||||
uint32_t flags)
|
||||
: flags_(flags),
|
||||
generation_(0),
|
||||
content_name_(content_name),
|
||||
component_(component),
|
||||
ice_ufrag_(ice_ufrag),
|
||||
ice_pwd_(ice_pwd) {
|
||||
// Pooled sessions are allowed to be created with empty content name,
|
||||
// component, ufrag and password.
|
||||
RTC_DCHECK(ice_ufrag.empty() == ice_pwd.empty());
|
||||
}
|
||||
|
||||
PortAllocatorSession::~PortAllocatorSession() = default;
|
||||
|
||||
bool PortAllocatorSession::IsCleared() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PortAllocatorSession::IsStopped() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t PortAllocatorSession::generation() {
|
||||
return generation_;
|
||||
}
|
||||
|
||||
void PortAllocatorSession::set_generation(uint32_t generation) {
|
||||
generation_ = generation;
|
||||
}
|
||||
|
||||
PortAllocator::PortAllocator()
|
||||
: flags_(kDefaultPortAllocatorFlags),
|
||||
min_port_(0),
|
||||
max_port_(0),
|
||||
max_ipv6_networks_(kDefaultMaxIPv6Networks),
|
||||
step_delay_(kDefaultStepDelay),
|
||||
allow_tcp_listen_(true),
|
||||
candidate_filter_(CF_ALL),
|
||||
tiebreaker_(rtc::CreateRandomId64()) {
|
||||
// The allocator will be attached to a thread in Initialize.
|
||||
thread_checker_.Detach();
|
||||
}
|
||||
|
||||
void PortAllocator::Initialize() {
|
||||
RTC_DCHECK(thread_checker_.IsCurrent());
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
PortAllocator::~PortAllocator() {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
}
|
||||
|
||||
void PortAllocator::set_restrict_ice_credentials_change(bool value) {
|
||||
restrict_ice_credentials_change_ = value;
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
bool PortAllocator::SetConfiguration(
|
||||
const ServerAddresses& stun_servers,
|
||||
const std::vector<RelayServerConfig>& turn_servers,
|
||||
int candidate_pool_size,
|
||||
bool prune_turn_ports,
|
||||
webrtc::TurnCustomizer* turn_customizer,
|
||||
const absl::optional<int>& stun_candidate_keepalive_interval) {
|
||||
webrtc::PortPrunePolicy turn_port_prune_policy =
|
||||
prune_turn_ports ? webrtc::PRUNE_BASED_ON_PRIORITY : webrtc::NO_PRUNE;
|
||||
return SetConfiguration(stun_servers, turn_servers, candidate_pool_size,
|
||||
turn_port_prune_policy, turn_customizer,
|
||||
stun_candidate_keepalive_interval);
|
||||
}
|
||||
|
||||
bool PortAllocator::SetConfiguration(
|
||||
const ServerAddresses& stun_servers,
|
||||
const std::vector<RelayServerConfig>& turn_servers,
|
||||
int candidate_pool_size,
|
||||
webrtc::PortPrunePolicy turn_port_prune_policy,
|
||||
webrtc::TurnCustomizer* turn_customizer,
|
||||
const absl::optional<int>& stun_candidate_keepalive_interval) {
|
||||
RTC_DCHECK_GE(candidate_pool_size, 0);
|
||||
RTC_DCHECK_LE(candidate_pool_size, static_cast<int>(UINT16_MAX));
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
// A positive candidate pool size would lead to the creation of a pooled
|
||||
// allocator session and starting getting ports, which we should only do on
|
||||
// the network thread.
|
||||
RTC_DCHECK(candidate_pool_size == 0 || thread_checker_.IsCurrent());
|
||||
bool ice_servers_changed =
|
||||
(stun_servers != stun_servers_ || turn_servers != turn_servers_);
|
||||
stun_servers_ = stun_servers;
|
||||
turn_servers_ = turn_servers;
|
||||
turn_port_prune_policy_ = turn_port_prune_policy;
|
||||
|
||||
candidate_pool_size_ = candidate_pool_size;
|
||||
|
||||
// If ICE servers changed, throw away any existing pooled sessions and create
|
||||
// new ones.
|
||||
if (ice_servers_changed) {
|
||||
pooled_sessions_.clear();
|
||||
}
|
||||
|
||||
turn_customizer_ = turn_customizer;
|
||||
|
||||
// If `candidate_pool_size_` is less than the number of pooled sessions, get
|
||||
// rid of the extras.
|
||||
while (candidate_pool_size_ < static_cast<int>(pooled_sessions_.size())) {
|
||||
pooled_sessions_.back().reset(nullptr);
|
||||
pooled_sessions_.pop_back();
|
||||
}
|
||||
|
||||
// `stun_candidate_keepalive_interval_` will be used in STUN port allocation
|
||||
// in future sessions. We also update the ready ports in the pooled sessions.
|
||||
// Ports in sessions that are taken and owned by P2PTransportChannel will be
|
||||
// updated there via IceConfig.
|
||||
stun_candidate_keepalive_interval_ = stun_candidate_keepalive_interval;
|
||||
for (const auto& session : pooled_sessions_) {
|
||||
session->SetStunKeepaliveIntervalForReadyPorts(
|
||||
stun_candidate_keepalive_interval_);
|
||||
}
|
||||
|
||||
// If `candidate_pool_size_` is greater than the number of pooled sessions,
|
||||
// create new sessions.
|
||||
while (static_cast<int>(pooled_sessions_.size()) < candidate_pool_size_) {
|
||||
IceParameters iceCredentials =
|
||||
IceCredentialsIterator::CreateRandomIceCredentials();
|
||||
PortAllocatorSession* pooled_session =
|
||||
CreateSessionInternal("", 0, iceCredentials.ufrag, iceCredentials.pwd);
|
||||
pooled_session->set_pooled(true);
|
||||
pooled_session->StartGettingPorts();
|
||||
pooled_sessions_.push_back(
|
||||
std::unique_ptr<PortAllocatorSession>(pooled_session));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<PortAllocatorSession> PortAllocator::CreateSession(
|
||||
absl::string_view content_name,
|
||||
int component,
|
||||
absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd) {
|
||||
CheckRunOnValidThreadAndInitialized();
|
||||
auto session = std::unique_ptr<PortAllocatorSession>(
|
||||
CreateSessionInternal(content_name, component, ice_ufrag, ice_pwd));
|
||||
session->SetCandidateFilter(candidate_filter());
|
||||
return session;
|
||||
}
|
||||
|
||||
std::unique_ptr<PortAllocatorSession> PortAllocator::TakePooledSession(
|
||||
absl::string_view content_name,
|
||||
int component,
|
||||
absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd) {
|
||||
CheckRunOnValidThreadAndInitialized();
|
||||
RTC_DCHECK(!ice_ufrag.empty());
|
||||
RTC_DCHECK(!ice_pwd.empty());
|
||||
if (pooled_sessions_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IceParameters credentials(ice_ufrag, ice_pwd, false);
|
||||
// If restrict_ice_credentials_change_ is TRUE, then call FindPooledSession
|
||||
// with ice credentials. Otherwise call it with nullptr which means
|
||||
// "find any" pooled session.
|
||||
auto cit = FindPooledSession(restrict_ice_credentials_change_ ? &credentials
|
||||
: nullptr);
|
||||
if (cit == pooled_sessions_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it =
|
||||
pooled_sessions_.begin() + std::distance(pooled_sessions_.cbegin(), cit);
|
||||
std::unique_ptr<PortAllocatorSession> ret = std::move(*it);
|
||||
ret->SetIceParameters(content_name, component, ice_ufrag, ice_pwd);
|
||||
ret->set_pooled(false);
|
||||
// According to JSEP, a pooled session should filter candidates only
|
||||
// after it's taken out of the pool.
|
||||
ret->SetCandidateFilter(candidate_filter());
|
||||
pooled_sessions_.erase(it);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const PortAllocatorSession* PortAllocator::GetPooledSession(
|
||||
const IceParameters* ice_credentials) const {
|
||||
CheckRunOnValidThreadAndInitialized();
|
||||
auto it = FindPooledSession(ice_credentials);
|
||||
if (it == pooled_sessions_.end()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return it->get();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<PortAllocatorSession>>::const_iterator
|
||||
PortAllocator::FindPooledSession(const IceParameters* ice_credentials) const {
|
||||
for (auto it = pooled_sessions_.begin(); it != pooled_sessions_.end(); ++it) {
|
||||
if (ice_credentials == nullptr ||
|
||||
((*it)->ice_ufrag() == ice_credentials->ufrag &&
|
||||
(*it)->ice_pwd() == ice_credentials->pwd)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return pooled_sessions_.end();
|
||||
}
|
||||
|
||||
void PortAllocator::DiscardCandidatePool() {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
pooled_sessions_.clear();
|
||||
}
|
||||
|
||||
void PortAllocator::SetCandidateFilter(uint32_t filter) {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
if (candidate_filter_ == filter) {
|
||||
return;
|
||||
}
|
||||
uint32_t prev_filter = candidate_filter_;
|
||||
candidate_filter_ = filter;
|
||||
SignalCandidateFilterChanged(prev_filter, filter);
|
||||
}
|
||||
|
||||
void PortAllocator::GetCandidateStatsFromPooledSessions(
|
||||
CandidateStatsList* candidate_stats_list) {
|
||||
CheckRunOnValidThreadAndInitialized();
|
||||
for (const auto& session : pooled_sessions()) {
|
||||
session->GetCandidateStatsFromReadyPorts(candidate_stats_list);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<IceParameters> PortAllocator::GetPooledIceCredentials() {
|
||||
CheckRunOnValidThreadAndInitialized();
|
||||
std::vector<IceParameters> list;
|
||||
for (const auto& session : pooled_sessions_) {
|
||||
list.push_back(
|
||||
IceParameters(session->ice_ufrag(), session->ice_pwd(), false));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
Candidate PortAllocator::SanitizeCandidate(const Candidate& c) const {
|
||||
CheckRunOnValidThreadAndInitialized();
|
||||
// For a local host candidate, we need to conceal its IP address candidate if
|
||||
// the mDNS obfuscation is enabled.
|
||||
bool use_hostname_address =
|
||||
(c.is_local() || c.is_prflx()) && MdnsObfuscationEnabled();
|
||||
// If adapter enumeration is disabled or host candidates are disabled,
|
||||
// clear the raddr of STUN candidates to avoid local address leakage.
|
||||
bool filter_stun_related_address =
|
||||
((flags() & PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION) &&
|
||||
(flags() & PORTALLOCATOR_DISABLE_DEFAULT_LOCAL_CANDIDATE)) ||
|
||||
!(candidate_filter_ & CF_HOST) || MdnsObfuscationEnabled();
|
||||
// If the candidate filter doesn't allow reflexive addresses, empty TURN raddr
|
||||
// to avoid reflexive address leakage.
|
||||
bool filter_turn_related_address = !(candidate_filter_ & CF_REFLEXIVE);
|
||||
// Sanitize related_address when using MDNS.
|
||||
bool filter_prflx_related_address = MdnsObfuscationEnabled();
|
||||
bool filter_related_address =
|
||||
((c.is_stun() && filter_stun_related_address) ||
|
||||
(c.is_relay() && filter_turn_related_address) ||
|
||||
(c.is_prflx() && filter_prflx_related_address));
|
||||
return c.ToSanitizedCopy(use_hostname_address, filter_related_address);
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
673
TMessagesProj/jni/voip/webrtc/p2p/base/port_allocator.h
Normal file
673
TMessagesProj/jni/voip/webrtc/p2p/base/port_allocator.h
Normal file
|
|
@ -0,0 +1,673 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_PORT_ALLOCATOR_H_
|
||||
#define P2P_BASE_PORT_ALLOCATOR_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/candidate.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/transport/enums.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "p2p/base/port_interface.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/helpers.h"
|
||||
#include "rtc_base/network.h"
|
||||
#include "rtc_base/proxy_info.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/ssl_certificate.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
#include "rtc_base/thread.h"
|
||||
|
||||
namespace webrtc {
|
||||
class TurnCustomizer;
|
||||
} // namespace webrtc
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// PortAllocator is responsible for allocating Port types for a given
|
||||
// P2PSocket. It also handles port freeing.
|
||||
//
|
||||
// Clients can override this class to control port allocation, including
|
||||
// what kinds of ports are allocated.
|
||||
|
||||
enum {
|
||||
// Disable local UDP ports. This doesn't impact how we connect to relay
|
||||
// servers.
|
||||
PORTALLOCATOR_DISABLE_UDP = 0x01,
|
||||
PORTALLOCATOR_DISABLE_STUN = 0x02,
|
||||
PORTALLOCATOR_DISABLE_RELAY = 0x04,
|
||||
// Disable local TCP ports. This doesn't impact how we connect to relay
|
||||
// servers.
|
||||
PORTALLOCATOR_DISABLE_TCP = 0x08,
|
||||
PORTALLOCATOR_ENABLE_IPV6 = 0x40,
|
||||
PORTALLOCATOR_ENABLE_SHARED_SOCKET = 0x100,
|
||||
PORTALLOCATOR_ENABLE_STUN_RETRANSMIT_ATTRIBUTE = 0x200,
|
||||
// When specified, we'll only allocate the STUN candidate for the public
|
||||
// interface as seen by regular http traffic and the HOST candidate associated
|
||||
// with the default local interface.
|
||||
PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION = 0x400,
|
||||
// When specified along with PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION, the
|
||||
// default local candidate mentioned above will not be allocated. Only the
|
||||
// STUN candidate will be.
|
||||
PORTALLOCATOR_DISABLE_DEFAULT_LOCAL_CANDIDATE = 0x800,
|
||||
// Disallow use of UDP when connecting to a relay server. Since proxy servers
|
||||
// usually don't handle UDP, using UDP will leak the IP address.
|
||||
PORTALLOCATOR_DISABLE_UDP_RELAY = 0x1000,
|
||||
|
||||
// When multiple networks exist, do not gather candidates on the ones with
|
||||
// high cost. So if both Wi-Fi and cellular networks exist, gather only on the
|
||||
// Wi-Fi network. If a network type is "unknown", it has a cost lower than
|
||||
// cellular but higher than Wi-Fi/Ethernet. So if an unknown network exists,
|
||||
// cellular networks will not be used to gather candidates and if a Wi-Fi
|
||||
// network is present, "unknown" networks will not be usd to gather
|
||||
// candidates. Doing so ensures that even if a cellular network type was not
|
||||
// detected initially, it would not be used if a Wi-Fi network is present.
|
||||
PORTALLOCATOR_DISABLE_COSTLY_NETWORKS = 0x2000,
|
||||
|
||||
// When specified, do not collect IPv6 ICE candidates on Wi-Fi.
|
||||
PORTALLOCATOR_ENABLE_IPV6_ON_WIFI = 0x4000,
|
||||
|
||||
// When this flag is set, ports not bound to any specific network interface
|
||||
// will be used, in addition to normal ports bound to the enumerated
|
||||
// interfaces. Without this flag, these "any address" ports would only be
|
||||
// used when network enumeration fails or is disabled. But under certain
|
||||
// conditions, these ports may succeed where others fail, so they may allow
|
||||
// the application to work in a wider variety of environments, at the expense
|
||||
// of having to allocate additional candidates.
|
||||
PORTALLOCATOR_ENABLE_ANY_ADDRESS_PORTS = 0x8000,
|
||||
|
||||
// Exclude link-local network interfaces
|
||||
// from considertaion after adapter enumeration.
|
||||
PORTALLOCATOR_DISABLE_LINK_LOCAL_NETWORKS = 0x10000,
|
||||
};
|
||||
|
||||
// Defines various reasons that have caused ICE regathering.
|
||||
enum class IceRegatheringReason {
|
||||
NETWORK_CHANGE, // Network interfaces on the device changed
|
||||
NETWORK_FAILURE, // Regather only on networks that have failed
|
||||
OCCASIONAL_REFRESH, // Periodic regather on all networks
|
||||
MAX_VALUE
|
||||
};
|
||||
|
||||
const uint32_t kDefaultPortAllocatorFlags = 0;
|
||||
|
||||
const uint32_t kDefaultStepDelay = 1000; // 1 sec step delay.
|
||||
// As per RFC 5245 Appendix B.1, STUN transactions need to be paced at certain
|
||||
// internal. Less than 20ms is not acceptable. We choose 50ms as our default.
|
||||
const uint32_t kMinimumStepDelay = 50;
|
||||
|
||||
// Turning on IPv6 could make many IPv6 interfaces available for connectivity
|
||||
// check and delay the call setup time. kDefaultMaxIPv6Networks is the default
|
||||
// upper limit of IPv6 networks but could be changed by
|
||||
// set_max_ipv6_networks().
|
||||
constexpr int kDefaultMaxIPv6Networks = 5;
|
||||
|
||||
// CF = CANDIDATE FILTER
|
||||
enum : uint32_t {
|
||||
CF_NONE = 0x0,
|
||||
CF_HOST = 0x1,
|
||||
CF_REFLEXIVE = 0x2,
|
||||
CF_RELAY = 0x4,
|
||||
CF_ALL = 0x7,
|
||||
};
|
||||
|
||||
// TLS certificate policy.
|
||||
enum class TlsCertPolicy {
|
||||
// For TLS based protocols, ensure the connection is secure by not
|
||||
// circumventing certificate validation.
|
||||
TLS_CERT_POLICY_SECURE,
|
||||
// For TLS based protocols, disregard security completely by skipping
|
||||
// certificate validation. This is insecure and should never be used unless
|
||||
// security is irrelevant in that particular context.
|
||||
TLS_CERT_POLICY_INSECURE_NO_CHECK,
|
||||
};
|
||||
|
||||
// TODO(deadbeef): Rename to TurnCredentials (and username to ufrag).
|
||||
struct RelayCredentials {
|
||||
RelayCredentials() {}
|
||||
RelayCredentials(absl::string_view username, absl::string_view password)
|
||||
: username(username), password(password) {}
|
||||
|
||||
bool operator==(const RelayCredentials& o) const {
|
||||
return username == o.username && password == o.password;
|
||||
}
|
||||
bool operator!=(const RelayCredentials& o) const { return !(*this == o); }
|
||||
|
||||
std::string username;
|
||||
std::string password;
|
||||
};
|
||||
|
||||
typedef std::vector<ProtocolAddress> PortList;
|
||||
// TODO(deadbeef): Rename to TurnServerConfig.
|
||||
struct RTC_EXPORT RelayServerConfig {
|
||||
RelayServerConfig();
|
||||
RelayServerConfig(const rtc::SocketAddress& address,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
ProtocolType proto);
|
||||
RelayServerConfig(absl::string_view address,
|
||||
int port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
ProtocolType proto);
|
||||
// Legacy constructor where "secure" and PROTO_TCP implies PROTO_TLS.
|
||||
RelayServerConfig(absl::string_view address,
|
||||
int port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
ProtocolType proto,
|
||||
bool secure);
|
||||
RelayServerConfig(const RelayServerConfig&);
|
||||
~RelayServerConfig();
|
||||
|
||||
bool operator==(const RelayServerConfig& o) const {
|
||||
return ports == o.ports && credentials == o.credentials;
|
||||
}
|
||||
bool operator!=(const RelayServerConfig& o) const { return !(*this == o); }
|
||||
|
||||
PortList ports;
|
||||
RelayCredentials credentials;
|
||||
TlsCertPolicy tls_cert_policy = TlsCertPolicy::TLS_CERT_POLICY_SECURE;
|
||||
std::vector<std::string> tls_alpn_protocols;
|
||||
std::vector<std::string> tls_elliptic_curves;
|
||||
rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr;
|
||||
std::string turn_logging_id;
|
||||
};
|
||||
|
||||
class RTC_EXPORT PortAllocatorSession : public sigslot::has_slots<> {
|
||||
public:
|
||||
// Content name passed in mostly for logging and debugging.
|
||||
PortAllocatorSession(absl::string_view content_name,
|
||||
int component,
|
||||
absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd,
|
||||
uint32_t flags);
|
||||
|
||||
// Subclasses should clean up any ports created.
|
||||
~PortAllocatorSession() override;
|
||||
|
||||
uint32_t flags() const { return flags_; }
|
||||
void set_flags(uint32_t flags) { flags_ = flags; }
|
||||
std::string content_name() const { return content_name_; }
|
||||
int component() const { return component_; }
|
||||
const std::string& ice_ufrag() const { return ice_ufrag_; }
|
||||
const std::string& ice_pwd() const { return ice_pwd_; }
|
||||
bool pooled() const { return pooled_; }
|
||||
|
||||
// Setting this filter should affect not only candidates gathered in the
|
||||
// future, but candidates already gathered and ports already "ready",
|
||||
// which would be returned by ReadyCandidates() and ReadyPorts().
|
||||
//
|
||||
// Default filter should be CF_ALL.
|
||||
virtual void SetCandidateFilter(uint32_t filter) = 0;
|
||||
|
||||
// Starts gathering ports and ICE candidates.
|
||||
virtual void StartGettingPorts() = 0;
|
||||
// Completely stops gathering. Will not gather again unless StartGettingPorts
|
||||
// is called again.
|
||||
virtual void StopGettingPorts() = 0;
|
||||
// Whether the session is actively getting ports.
|
||||
virtual bool IsGettingPorts() = 0;
|
||||
|
||||
//
|
||||
// NOTE: The group of methods below is only used for continual gathering.
|
||||
//
|
||||
|
||||
// ClearGettingPorts should have the same immediate effect as
|
||||
// StopGettingPorts, but if the implementation supports continual gathering,
|
||||
// ClearGettingPorts allows additional ports/candidates to be gathered if the
|
||||
// network conditions change.
|
||||
virtual void ClearGettingPorts() = 0;
|
||||
// Whether it is in the state where the existing gathering process is stopped,
|
||||
// but new ones may be started (basically after calling ClearGettingPorts).
|
||||
virtual bool IsCleared() const;
|
||||
// Whether the session has completely stopped.
|
||||
virtual bool IsStopped() const;
|
||||
// Re-gathers candidates on networks that do not have any connections. More
|
||||
// precisely, a network interface may have more than one IP addresses (e.g.,
|
||||
// IPv4 and IPv6 addresses). Each address subnet will be used to create a
|
||||
// network. Only if all networks of an interface have no connection, the
|
||||
// implementation should start re-gathering on all networks of that interface.
|
||||
virtual void RegatherOnFailedNetworks() {}
|
||||
// Get candidate-level stats from all candidates on the ready ports and return
|
||||
// the stats to the given list.
|
||||
virtual void GetCandidateStatsFromReadyPorts(
|
||||
CandidateStatsList* candidate_stats_list) const {}
|
||||
// Set the interval at which STUN candidates will resend STUN binding requests
|
||||
// on the underlying ports to keep NAT bindings open.
|
||||
// The default value of the interval in implementation is restored if a null
|
||||
// optional value is passed.
|
||||
virtual void SetStunKeepaliveIntervalForReadyPorts(
|
||||
const absl::optional<int>& stun_keepalive_interval) {}
|
||||
// Another way of getting the information provided by the signals below.
|
||||
//
|
||||
// Ports and candidates are not guaranteed to be in the same order as the
|
||||
// signals were emitted in.
|
||||
virtual std::vector<PortInterface*> ReadyPorts() const = 0;
|
||||
virtual std::vector<Candidate> ReadyCandidates() const = 0;
|
||||
virtual bool CandidatesAllocationDone() const = 0;
|
||||
// Marks all ports in the current session as "pruned" so that they may be
|
||||
// destroyed if no connection is using them.
|
||||
virtual void PruneAllPorts() {}
|
||||
|
||||
sigslot::signal2<PortAllocatorSession*, PortInterface*> SignalPortReady;
|
||||
// Fires this signal when the network of the ports failed (either because the
|
||||
// interface is down, or because there is no connection on the interface),
|
||||
// or when TURN ports are pruned because a higher-priority TURN port becomes
|
||||
// ready(pairable).
|
||||
sigslot::signal2<PortAllocatorSession*, const std::vector<PortInterface*>&>
|
||||
SignalPortsPruned;
|
||||
sigslot::signal2<PortAllocatorSession*, const std::vector<Candidate>&>
|
||||
SignalCandidatesReady;
|
||||
sigslot::signal2<PortAllocatorSession*, const IceCandidateErrorEvent&>
|
||||
SignalCandidateError;
|
||||
// Candidates should be signaled to be removed when the port that generated
|
||||
// the candidates is removed.
|
||||
sigslot::signal2<PortAllocatorSession*, const std::vector<Candidate>&>
|
||||
SignalCandidatesRemoved;
|
||||
sigslot::signal1<PortAllocatorSession*> SignalCandidatesAllocationDone;
|
||||
|
||||
sigslot::signal2<PortAllocatorSession*, IceRegatheringReason>
|
||||
SignalIceRegathering;
|
||||
|
||||
virtual uint32_t generation();
|
||||
virtual void set_generation(uint32_t generation);
|
||||
|
||||
protected:
|
||||
// This method is called when a pooled session (which doesn't have these
|
||||
// properties initially) is returned by PortAllocator::TakePooledSession,
|
||||
// and the content name, component, and ICE ufrag/pwd are updated.
|
||||
//
|
||||
// A subclass may need to override this method to perform additional actions,
|
||||
// such as applying the updated information to ports and candidates.
|
||||
virtual void UpdateIceParametersInternal() {}
|
||||
|
||||
// TODO(deadbeef): Get rid of these when everyone switches to ice_ufrag and
|
||||
// ice_pwd.
|
||||
const std::string& username() const { return ice_ufrag_; }
|
||||
const std::string& password() const { return ice_pwd_; }
|
||||
|
||||
private:
|
||||
void SetIceParameters(absl::string_view content_name,
|
||||
int component,
|
||||
absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd) {
|
||||
content_name_ = std::string(content_name);
|
||||
component_ = component;
|
||||
ice_ufrag_ = std::string(ice_ufrag);
|
||||
ice_pwd_ = std::string(ice_pwd);
|
||||
UpdateIceParametersInternal();
|
||||
}
|
||||
|
||||
void set_pooled(bool value) { pooled_ = value; }
|
||||
|
||||
uint32_t flags_;
|
||||
uint32_t generation_;
|
||||
std::string content_name_;
|
||||
int component_;
|
||||
std::string ice_ufrag_;
|
||||
std::string ice_pwd_;
|
||||
|
||||
bool pooled_ = false;
|
||||
|
||||
// SetIceParameters is an implementation detail which only PortAllocator
|
||||
// should be able to call.
|
||||
friend class PortAllocator;
|
||||
};
|
||||
|
||||
// Every method of PortAllocator (including the destructor) must be called on
|
||||
// the same thread after Initialize is called.
|
||||
//
|
||||
// This allows a PortAllocator subclass to be constructed and configured on one
|
||||
// thread, and passed into an object that uses it on a different thread.
|
||||
class RTC_EXPORT PortAllocator : public sigslot::has_slots<> {
|
||||
public:
|
||||
PortAllocator();
|
||||
~PortAllocator() override;
|
||||
|
||||
// This MUST be called on the PortAllocator's thread after finishing
|
||||
// constructing and configuring the PortAllocator subclasses.
|
||||
virtual void Initialize();
|
||||
|
||||
// Set to true if some Ports need to know the ICE credentials when they are
|
||||
// created. This will ensure that the PortAllocator will only match pooled
|
||||
// allocator sessions to the ICE transport with the same credentials.
|
||||
virtual void set_restrict_ice_credentials_change(bool value);
|
||||
|
||||
// Set STUN and TURN servers to be used in future sessions, and set
|
||||
// candidate pool size, as described in JSEP.
|
||||
//
|
||||
// If the servers are changing, and the candidate pool size is nonzero, and
|
||||
// FreezeCandidatePool hasn't been called, existing pooled sessions will be
|
||||
// destroyed and new ones created.
|
||||
//
|
||||
// If the servers are not changing but the candidate pool size is, and
|
||||
// FreezeCandidatePool hasn't been called, pooled sessions will be either
|
||||
// created or destroyed as necessary.
|
||||
//
|
||||
// Returns true if the configuration could successfully be changed.
|
||||
// Deprecated
|
||||
bool SetConfiguration(const ServerAddresses& stun_servers,
|
||||
const std::vector<RelayServerConfig>& turn_servers,
|
||||
int candidate_pool_size,
|
||||
bool prune_turn_ports,
|
||||
webrtc::TurnCustomizer* turn_customizer = nullptr,
|
||||
const absl::optional<int>&
|
||||
stun_candidate_keepalive_interval = absl::nullopt);
|
||||
bool SetConfiguration(const ServerAddresses& stun_servers,
|
||||
const std::vector<RelayServerConfig>& turn_servers,
|
||||
int candidate_pool_size,
|
||||
webrtc::PortPrunePolicy turn_port_prune_policy,
|
||||
webrtc::TurnCustomizer* turn_customizer = nullptr,
|
||||
const absl::optional<int>&
|
||||
stun_candidate_keepalive_interval = absl::nullopt);
|
||||
|
||||
const ServerAddresses& stun_servers() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return stun_servers_;
|
||||
}
|
||||
|
||||
const std::vector<RelayServerConfig>& turn_servers() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return turn_servers_;
|
||||
}
|
||||
|
||||
int candidate_pool_size() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return candidate_pool_size_;
|
||||
}
|
||||
|
||||
const absl::optional<int>& stun_candidate_keepalive_interval() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return stun_candidate_keepalive_interval_;
|
||||
}
|
||||
|
||||
// Sets the network types to ignore.
|
||||
// Values are defined by the AdapterType enum.
|
||||
// For instance, calling this with
|
||||
// ADAPTER_TYPE_ETHERNET | ADAPTER_TYPE_LOOPBACK will ignore Ethernet and
|
||||
// loopback interfaces.
|
||||
virtual void SetNetworkIgnoreMask(int network_ignore_mask) = 0;
|
||||
|
||||
// Set whether VPN connections should be preferred, avoided, mandated or
|
||||
// blocked.
|
||||
virtual void SetVpnPreference(webrtc::VpnPreference preference) {
|
||||
vpn_preference_ = preference;
|
||||
}
|
||||
|
||||
// Set list of <ipaddress, mask> that shall be categorized as VPN.
|
||||
// Implemented by BasicPortAllocator.
|
||||
virtual void SetVpnList(const std::vector<rtc::NetworkMask>& vpn_list) {}
|
||||
|
||||
std::unique_ptr<PortAllocatorSession> CreateSession(
|
||||
absl::string_view content_name,
|
||||
int component,
|
||||
absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd);
|
||||
|
||||
// Get an available pooled session and set the transport information on it.
|
||||
//
|
||||
// Caller takes ownership of the returned session.
|
||||
//
|
||||
// If restrict_ice_credentials_change is TRUE, then it will only
|
||||
// return a pooled session with matching ice credentials.
|
||||
// If no pooled sessions are available, returns null.
|
||||
std::unique_ptr<PortAllocatorSession> TakePooledSession(
|
||||
absl::string_view content_name,
|
||||
int component,
|
||||
absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd);
|
||||
|
||||
// Returns the next session that would be returned by TakePooledSession
|
||||
// optionally restricting it to sessions with specified ice credentials.
|
||||
const PortAllocatorSession* GetPooledSession(
|
||||
const IceParameters* ice_credentials = nullptr) const;
|
||||
|
||||
// Discard any remaining pooled sessions.
|
||||
void DiscardCandidatePool();
|
||||
|
||||
// Clears the address and the related address fields of a local candidate to
|
||||
// avoid IP leakage. This is applicable in several scenarios:
|
||||
// 1. Sanitization is configured via the candidate filter.
|
||||
// 2. Sanitization is configured via the port allocator flags.
|
||||
// 3. mDNS concealment of private IPs is enabled.
|
||||
Candidate SanitizeCandidate(const Candidate& c) const;
|
||||
|
||||
uint64_t ice_tiebreaker() const { return tiebreaker_; }
|
||||
|
||||
uint32_t flags() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return flags_;
|
||||
}
|
||||
|
||||
void set_flags(uint32_t flags) {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
flags_ = flags;
|
||||
}
|
||||
|
||||
// These three methods are deprecated. If connections need to go through a
|
||||
// proxy, the application should create a BasicPortAllocator given a custom
|
||||
// PacketSocketFactory that creates proxy sockets.
|
||||
const std::string& user_agent() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return agent_;
|
||||
}
|
||||
|
||||
const rtc::ProxyInfo& proxy() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return proxy_;
|
||||
}
|
||||
|
||||
void set_proxy(absl::string_view agent, const rtc::ProxyInfo& proxy) {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
agent_ = std::string(agent);
|
||||
proxy_ = proxy;
|
||||
}
|
||||
|
||||
// Gets/Sets the port range to use when choosing client ports.
|
||||
int min_port() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return min_port_;
|
||||
}
|
||||
|
||||
int max_port() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return max_port_;
|
||||
}
|
||||
|
||||
bool SetPortRange(int min_port, int max_port) {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
if (min_port > max_port) {
|
||||
return false;
|
||||
}
|
||||
|
||||
min_port_ = min_port;
|
||||
max_port_ = max_port;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Can be used to change the default numer of IPv6 network interfaces used
|
||||
// (5). Can set to INT_MAX to effectively disable the limit.
|
||||
//
|
||||
// TODO(deadbeef): Applications shouldn't have to arbitrarily limit the
|
||||
// number of available IPv6 network interfaces just because they could slow
|
||||
// ICE down. We should work on making our ICE logic smarter (for example,
|
||||
// prioritizing pinging connections that are most likely to work) so that
|
||||
// every network interface can be used without impacting ICE's speed.
|
||||
void set_max_ipv6_networks(int networks) {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
max_ipv6_networks_ = networks;
|
||||
}
|
||||
|
||||
int max_ipv6_networks() {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return max_ipv6_networks_;
|
||||
}
|
||||
|
||||
// Delay between different candidate gathering phases (UDP, TURN, TCP).
|
||||
// Defaults to 1 second, but PeerConnection sets it to 50ms.
|
||||
// TODO(deadbeef): Get rid of this. Its purpose is to avoid sending too many
|
||||
// STUN transactions at once, but that's already happening if you configure
|
||||
// multiple STUN servers or have multiple network interfaces. We should
|
||||
// implement some global pacing logic instead if that's our goal.
|
||||
uint32_t step_delay() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return step_delay_;
|
||||
}
|
||||
|
||||
void set_step_delay(uint32_t delay) {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
step_delay_ = delay;
|
||||
}
|
||||
|
||||
bool allow_tcp_listen() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return allow_tcp_listen_;
|
||||
}
|
||||
|
||||
void set_allow_tcp_listen(bool allow_tcp_listen) {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
allow_tcp_listen_ = allow_tcp_listen;
|
||||
}
|
||||
|
||||
uint32_t candidate_filter() {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return candidate_filter_;
|
||||
}
|
||||
|
||||
// The new filter value will be populated to future allocation sessions, when
|
||||
// they are created via CreateSession, and also pooled sessions when one is
|
||||
// taken via TakePooledSession.
|
||||
//
|
||||
// A change in the candidate filter also fires a signal
|
||||
// `SignalCandidateFilterChanged`, so that objects subscribed to this signal
|
||||
// can, for example, update the candidate filter for sessions created by this
|
||||
// allocator and already taken by the object.
|
||||
//
|
||||
// Specifically for the session taken by the ICE transport, we currently do
|
||||
// not support removing candidate pairs formed with local candidates from this
|
||||
// session that are disabled by the new candidate filter.
|
||||
void SetCandidateFilter(uint32_t filter);
|
||||
// Deprecated.
|
||||
// TODO(qingsi): Remove this after Chromium migrates to the new method.
|
||||
void set_candidate_filter(uint32_t filter) { SetCandidateFilter(filter); }
|
||||
|
||||
// Deprecated (by the next method).
|
||||
bool prune_turn_ports() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return turn_port_prune_policy_ == webrtc::PRUNE_BASED_ON_PRIORITY;
|
||||
}
|
||||
|
||||
webrtc::PortPrunePolicy turn_port_prune_policy() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return turn_port_prune_policy_;
|
||||
}
|
||||
|
||||
webrtc::TurnCustomizer* turn_customizer() {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return turn_customizer_;
|
||||
}
|
||||
|
||||
// Collect candidate stats from pooled allocator sessions. This can be used to
|
||||
// collect candidate stats without creating an offer/answer or setting local
|
||||
// description. After the local description is set, the ownership of the
|
||||
// pooled session is taken by P2PTransportChannel, and the
|
||||
// candidate stats can be collected from P2PTransportChannel::GetStats.
|
||||
virtual void GetCandidateStatsFromPooledSessions(
|
||||
CandidateStatsList* candidate_stats_list);
|
||||
|
||||
// Return IceParameters of the pooled sessions.
|
||||
std::vector<IceParameters> GetPooledIceCredentials();
|
||||
|
||||
// Fired when `candidate_filter_` changes.
|
||||
sigslot::signal2<uint32_t /* prev_filter */, uint32_t /* cur_filter */>
|
||||
SignalCandidateFilterChanged;
|
||||
|
||||
protected:
|
||||
// TODO(webrtc::13579): Remove std::string version once downstream users have
|
||||
// migrated to the absl::string_view version.
|
||||
virtual PortAllocatorSession* CreateSessionInternal(
|
||||
absl::string_view content_name,
|
||||
int component,
|
||||
absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd) = 0;
|
||||
|
||||
const std::vector<std::unique_ptr<PortAllocatorSession>>& pooled_sessions() {
|
||||
return pooled_sessions_;
|
||||
}
|
||||
|
||||
// Returns true if there is an mDNS responder attached to the network manager.
|
||||
virtual bool MdnsObfuscationEnabled() const { return false; }
|
||||
|
||||
// The following thread checks are only done in DCHECK for the consistency
|
||||
// with the exsiting thread checks.
|
||||
void CheckRunOnValidThreadIfInitialized() const {
|
||||
RTC_DCHECK(!initialized_ || thread_checker_.IsCurrent());
|
||||
}
|
||||
|
||||
void CheckRunOnValidThreadAndInitialized() const {
|
||||
RTC_DCHECK(initialized_ && thread_checker_.IsCurrent());
|
||||
}
|
||||
|
||||
bool initialized_ = false;
|
||||
uint32_t flags_;
|
||||
std::string agent_;
|
||||
rtc::ProxyInfo proxy_;
|
||||
int min_port_;
|
||||
int max_port_;
|
||||
int max_ipv6_networks_;
|
||||
uint32_t step_delay_;
|
||||
bool allow_tcp_listen_;
|
||||
uint32_t candidate_filter_;
|
||||
std::string origin_;
|
||||
webrtc::SequenceChecker thread_checker_;
|
||||
webrtc::VpnPreference vpn_preference_ = webrtc::VpnPreference::kDefault;
|
||||
|
||||
private:
|
||||
ServerAddresses stun_servers_;
|
||||
std::vector<RelayServerConfig> turn_servers_;
|
||||
int candidate_pool_size_ = 0; // Last value passed into SetConfiguration.
|
||||
std::vector<std::unique_ptr<PortAllocatorSession>> pooled_sessions_;
|
||||
webrtc::PortPrunePolicy turn_port_prune_policy_ = webrtc::NO_PRUNE;
|
||||
|
||||
// Customizer for TURN messages.
|
||||
// The instance is owned by application and will be shared among
|
||||
// all TurnPort(s) created.
|
||||
webrtc::TurnCustomizer* turn_customizer_ = nullptr;
|
||||
|
||||
absl::optional<int> stun_candidate_keepalive_interval_;
|
||||
|
||||
// If true, TakePooledSession() will only return sessions that has same ice
|
||||
// credentials as requested.
|
||||
bool restrict_ice_credentials_change_ = false;
|
||||
|
||||
// Returns iterator to pooled session with specified ice_credentials or first
|
||||
// if ice_credentials is nullptr.
|
||||
std::vector<std::unique_ptr<PortAllocatorSession>>::const_iterator
|
||||
FindPooledSession(const IceParameters* ice_credentials = nullptr) const;
|
||||
|
||||
// ICE tie breaker.
|
||||
uint64_t tiebreaker_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_PORT_ALLOCATOR_H_
|
||||
23
TMessagesProj/jni/voip/webrtc/p2p/base/port_interface.cc
Normal file
23
TMessagesProj/jni/voip/webrtc/p2p/base/port_interface.cc
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2017 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/port_interface.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
PortInterface::PortInterface() = default;
|
||||
|
||||
PortInterface::~PortInterface() = default;
|
||||
|
||||
} // namespace cricket
|
||||
221
TMessagesProj/jni/voip/webrtc/p2p/base/port_interface.h
Normal file
221
TMessagesProj/jni/voip/webrtc/p2p/base/port_interface.h
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* Copyright 2012 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_PORT_INTERFACE_H_
|
||||
#define P2P_BASE_PORT_INTERFACE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/candidate.h"
|
||||
#include "api/packet_socket_factory.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/callback_list.h"
|
||||
#include "rtc_base/proxy_info.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
|
||||
namespace rtc {
|
||||
class Network;
|
||||
struct PacketOptions;
|
||||
} // namespace rtc
|
||||
|
||||
namespace cricket {
|
||||
class Connection;
|
||||
class IceMessage;
|
||||
class StunMessage;
|
||||
class StunStats;
|
||||
|
||||
enum ProtocolType {
|
||||
PROTO_UDP,
|
||||
PROTO_TCP,
|
||||
PROTO_SSLTCP, // Pseudo-TLS.
|
||||
PROTO_TLS,
|
||||
PROTO_LAST = PROTO_TLS
|
||||
};
|
||||
|
||||
// Defines the interface for a port, which represents a local communication
|
||||
// mechanism that can be used to create connections to similar mechanisms of
|
||||
// the other client. Various types of ports will implement this interface.
|
||||
class PortInterface {
|
||||
public:
|
||||
virtual ~PortInterface();
|
||||
|
||||
virtual const absl::string_view Type() const = 0;
|
||||
virtual const rtc::Network* Network() const = 0;
|
||||
|
||||
// Methods to set/get ICE role and tiebreaker values.
|
||||
virtual void SetIceRole(IceRole role) = 0;
|
||||
virtual IceRole GetIceRole() const = 0;
|
||||
|
||||
virtual void SetIceTiebreaker(uint64_t tiebreaker) = 0;
|
||||
virtual uint64_t IceTiebreaker() const = 0;
|
||||
|
||||
virtual bool SharedSocket() const = 0;
|
||||
|
||||
virtual bool SupportsProtocol(absl::string_view protocol) const = 0;
|
||||
|
||||
// PrepareAddress will attempt to get an address for this port that other
|
||||
// clients can send to. It may take some time before the address is ready.
|
||||
// Once it is ready, we will send SignalAddressReady. If errors are
|
||||
// preventing the port from getting an address, it may send
|
||||
// SignalAddressError.
|
||||
virtual void PrepareAddress() = 0;
|
||||
|
||||
// Returns the connection to the given address or NULL if none exists.
|
||||
virtual Connection* GetConnection(const rtc::SocketAddress& remote_addr) = 0;
|
||||
|
||||
// Creates a new connection to the given address.
|
||||
enum CandidateOrigin { ORIGIN_THIS_PORT, ORIGIN_OTHER_PORT, ORIGIN_MESSAGE };
|
||||
virtual Connection* CreateConnection(const Candidate& remote_candidate,
|
||||
CandidateOrigin origin) = 0;
|
||||
|
||||
// Functions on the underlying socket(s).
|
||||
virtual int SetOption(rtc::Socket::Option opt, int value) = 0;
|
||||
virtual int GetOption(rtc::Socket::Option opt, int* value) = 0;
|
||||
virtual int GetError() = 0;
|
||||
|
||||
virtual ProtocolType GetProtocol() const = 0;
|
||||
|
||||
virtual const std::vector<Candidate>& Candidates() const = 0;
|
||||
|
||||
// Sends the given packet to the given address, provided that the address is
|
||||
// that of a connection or an address that has sent to us already.
|
||||
virtual int SendTo(const void* data,
|
||||
size_t size,
|
||||
const rtc::SocketAddress& addr,
|
||||
const rtc::PacketOptions& options,
|
||||
bool payload) = 0;
|
||||
|
||||
// Indicates that we received a successful STUN binding request from an
|
||||
// address that doesn't correspond to any current connection. To turn this
|
||||
// into a real connection, call CreateConnection.
|
||||
sigslot::signal6<PortInterface*,
|
||||
const rtc::SocketAddress&,
|
||||
ProtocolType,
|
||||
IceMessage*,
|
||||
const std::string&,
|
||||
bool>
|
||||
SignalUnknownAddress;
|
||||
|
||||
// Sends a response message (normal or error) to the given request. One of
|
||||
// these methods should be called as a response to SignalUnknownAddress.
|
||||
virtual void SendBindingErrorResponse(StunMessage* message,
|
||||
const rtc::SocketAddress& addr,
|
||||
int error_code,
|
||||
absl::string_view reason) = 0;
|
||||
|
||||
// Signaled when this port decides to delete itself because it no longer has
|
||||
// any usefulness.
|
||||
virtual void SubscribePortDestroyed(
|
||||
std::function<void(PortInterface*)> callback) = 0;
|
||||
|
||||
// Signaled when Port discovers ice role conflict with the peer.
|
||||
sigslot::signal1<PortInterface*> SignalRoleConflict;
|
||||
|
||||
// Normally, packets arrive through a connection (or they result signaling of
|
||||
// unknown address). Calling this method turns off delivery of packets
|
||||
// through their respective connection and instead delivers every packet
|
||||
// through this port.
|
||||
virtual void EnablePortPackets() = 0;
|
||||
sigslot::
|
||||
signal4<PortInterface*, const char*, size_t, const rtc::SocketAddress&>
|
||||
SignalReadPacket;
|
||||
|
||||
// Emitted each time a packet is sent on this port.
|
||||
sigslot::signal1<const rtc::SentPacket&> SignalSentPacket;
|
||||
|
||||
virtual std::string ToString() const = 0;
|
||||
|
||||
virtual void GetStunStats(absl::optional<StunStats>* stats) = 0;
|
||||
|
||||
// Removes and deletes a connection object. `DestroyConnection` will
|
||||
// delete the connection object directly whereas `DestroyConnectionAsync`
|
||||
// defers the `delete` operation to when the call stack has been unwound.
|
||||
// Async may be needed when deleting a connection object from within a
|
||||
// callback.
|
||||
virtual void DestroyConnection(Connection* conn) = 0;
|
||||
|
||||
virtual void DestroyConnectionAsync(Connection* conn) = 0;
|
||||
|
||||
// The thread on which this port performs its I/O.
|
||||
virtual webrtc::TaskQueueBase* thread() = 0;
|
||||
|
||||
// The factory used to create the sockets of this port.
|
||||
virtual rtc::PacketSocketFactory* socket_factory() const = 0;
|
||||
virtual const std::string& user_agent() = 0;
|
||||
virtual const rtc::ProxyInfo& proxy() = 0;
|
||||
|
||||
// Identifies the generation that this port was created in.
|
||||
virtual uint32_t generation() const = 0;
|
||||
virtual void set_generation(uint32_t generation) = 0;
|
||||
virtual bool send_retransmit_count_attribute() const = 0;
|
||||
// For debugging purposes.
|
||||
virtual const std::string& content_name() const = 0;
|
||||
|
||||
// Called when the Connection discovers a local peer reflexive candidate.
|
||||
virtual void AddPrflxCandidate(const Candidate& local) = 0;
|
||||
|
||||
// Foundation: An arbitrary string that is the same for two candidates
|
||||
// that have the same type, base IP address, protocol (UDP, TCP,
|
||||
// etc.), and STUN or TURN server. If any of these are different,
|
||||
// then the foundation will be different. Two candidate pairs with
|
||||
// the same foundation pairs are likely to have similar network
|
||||
// characteristics. Foundations are used in the frozen algorithm.
|
||||
virtual std::string ComputeFoundation(
|
||||
absl::string_view type,
|
||||
absl::string_view protocol,
|
||||
absl::string_view relay_protocol,
|
||||
const rtc::SocketAddress& base_address) = 0;
|
||||
|
||||
protected:
|
||||
PortInterface();
|
||||
virtual void UpdateNetworkCost() = 0;
|
||||
|
||||
// Returns DSCP value packets generated by the port itself should use.
|
||||
virtual rtc::DiffServCodePoint StunDscpValue() const = 0;
|
||||
|
||||
// If the given data comprises a complete and correct STUN message then the
|
||||
// return value is true, otherwise false. If the message username corresponds
|
||||
// with this port's username fragment, msg will contain the parsed STUN
|
||||
// message. Otherwise, the function may send a STUN response internally.
|
||||
// remote_username contains the remote fragment of the STUN username.
|
||||
virtual bool GetStunMessage(const char* data,
|
||||
size_t size,
|
||||
const rtc::SocketAddress& addr,
|
||||
std::unique_ptr<IceMessage>* out_msg,
|
||||
std::string* out_username) = 0;
|
||||
|
||||
// This method will return local and remote username fragements from the
|
||||
// stun username attribute if present.
|
||||
virtual bool ParseStunUsername(const StunMessage* stun_msg,
|
||||
std::string* local_username,
|
||||
std::string* remote_username) const = 0;
|
||||
virtual std::string CreateStunUsername(
|
||||
absl::string_view remote_username) const = 0;
|
||||
|
||||
virtual bool MaybeIceRoleConflict(const rtc::SocketAddress& addr,
|
||||
IceMessage* stun_msg,
|
||||
absl::string_view remote_ufrag) = 0;
|
||||
|
||||
virtual int16_t network_cost() const = 0;
|
||||
|
||||
// Connection and Port are entangled; functions exposed to Port only
|
||||
// should not be public.
|
||||
friend class Connection;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_PORT_INTERFACE_H_
|
||||
1434
TMessagesProj/jni/voip/webrtc/p2p/base/pseudo_tcp.cc
Normal file
1434
TMessagesProj/jni/voip/webrtc/p2p/base/pseudo_tcp.cc
Normal file
File diff suppressed because it is too large
Load diff
295
TMessagesProj/jni/voip/webrtc/p2p/base/pseudo_tcp.h
Normal file
295
TMessagesProj/jni/voip/webrtc/p2p/base/pseudo_tcp.h
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_PSEUDO_TCP_H_
|
||||
#define P2P_BASE_PSEUDO_TCP_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// IPseudoTcpNotify
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
class PseudoTcp;
|
||||
|
||||
class IPseudoTcpNotify {
|
||||
public:
|
||||
// Notification of tcp events
|
||||
virtual void OnTcpOpen(PseudoTcp* tcp) = 0;
|
||||
virtual void OnTcpReadable(PseudoTcp* tcp) = 0;
|
||||
virtual void OnTcpWriteable(PseudoTcp* tcp) = 0;
|
||||
virtual void OnTcpClosed(PseudoTcp* tcp, uint32_t error) = 0;
|
||||
|
||||
// Write the packet onto the network
|
||||
enum WriteResult { WR_SUCCESS, WR_TOO_LARGE, WR_FAIL };
|
||||
virtual WriteResult TcpWritePacket(PseudoTcp* tcp,
|
||||
const char* buffer,
|
||||
size_t len) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~IPseudoTcpNotify() {}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// PseudoTcp
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RTC_EXPORT PseudoTcp {
|
||||
public:
|
||||
static uint32_t Now();
|
||||
|
||||
PseudoTcp(IPseudoTcpNotify* notify, uint32_t conv);
|
||||
virtual ~PseudoTcp();
|
||||
|
||||
int Connect();
|
||||
int Recv(char* buffer, size_t len);
|
||||
int Send(const char* buffer, size_t len);
|
||||
void Close(bool force);
|
||||
int GetError();
|
||||
|
||||
enum TcpState {
|
||||
TCP_LISTEN,
|
||||
TCP_SYN_SENT,
|
||||
TCP_SYN_RECEIVED,
|
||||
TCP_ESTABLISHED,
|
||||
TCP_CLOSED
|
||||
};
|
||||
TcpState State() const { return m_state; }
|
||||
|
||||
// Call this when the PMTU changes.
|
||||
void NotifyMTU(uint16_t mtu);
|
||||
|
||||
// Call this based on timeout value returned from GetNextClock.
|
||||
// It's ok to call this too frequently.
|
||||
void NotifyClock(uint32_t now);
|
||||
|
||||
// Call this whenever a packet arrives.
|
||||
// Returns true if the packet was processed successfully.
|
||||
bool NotifyPacket(const char* buffer, size_t len);
|
||||
|
||||
// Call this to determine the next time NotifyClock should be called.
|
||||
// Returns false if the socket is ready to be destroyed.
|
||||
bool GetNextClock(uint32_t now, long& timeout);
|
||||
|
||||
// Call these to get/set option values to tailor this PseudoTcp
|
||||
// instance's behaviour for the kind of data it will carry.
|
||||
// If an unrecognized option is set or got, an assertion will fire.
|
||||
//
|
||||
// Setting options for OPT_RCVBUF or OPT_SNDBUF after Connect() is called
|
||||
// will result in an assertion.
|
||||
enum Option {
|
||||
OPT_NODELAY, // Whether to enable Nagle's algorithm (0 == off)
|
||||
OPT_ACKDELAY, // The Delayed ACK timeout (0 == off).
|
||||
OPT_RCVBUF, // Set the receive buffer size, in bytes.
|
||||
OPT_SNDBUF, // Set the send buffer size, in bytes.
|
||||
};
|
||||
void GetOption(Option opt, int* value);
|
||||
void SetOption(Option opt, int value);
|
||||
|
||||
// Returns current congestion window in bytes.
|
||||
uint32_t GetCongestionWindow() const;
|
||||
|
||||
// Returns amount of data in bytes that has been sent, but haven't
|
||||
// been acknowledged.
|
||||
uint32_t GetBytesInFlight() const;
|
||||
|
||||
// Returns number of bytes that were written in buffer and haven't
|
||||
// been sent.
|
||||
uint32_t GetBytesBufferedNotSent() const;
|
||||
|
||||
// Returns current round-trip time estimate in milliseconds.
|
||||
uint32_t GetRoundTripTimeEstimateMs() const;
|
||||
|
||||
protected:
|
||||
enum SendFlags { sfNone, sfDelayedAck, sfImmediateAck };
|
||||
|
||||
struct Segment {
|
||||
uint32_t conv, seq, ack;
|
||||
uint8_t flags;
|
||||
uint16_t wnd;
|
||||
const char* data;
|
||||
uint32_t len;
|
||||
uint32_t tsval, tsecr;
|
||||
};
|
||||
|
||||
struct SSegment {
|
||||
SSegment(uint32_t s, uint32_t l, bool c)
|
||||
: seq(s), len(l), /*tstamp(0),*/ xmit(0), bCtrl(c) {}
|
||||
uint32_t seq, len;
|
||||
// uint32_t tstamp;
|
||||
uint8_t xmit;
|
||||
bool bCtrl;
|
||||
};
|
||||
typedef std::list<SSegment> SList;
|
||||
|
||||
struct RSegment {
|
||||
uint32_t seq, len;
|
||||
};
|
||||
|
||||
uint32_t queue(const char* data, uint32_t len, bool bCtrl);
|
||||
|
||||
// Creates a packet and submits it to the network. This method can either
|
||||
// send payload or just an ACK packet.
|
||||
//
|
||||
// `seq` is the sequence number of this packet.
|
||||
// `flags` is the flags for sending this packet.
|
||||
// `offset` is the offset to read from `m_sbuf`.
|
||||
// `len` is the number of bytes to read from `m_sbuf` as payload. If this
|
||||
// value is 0 then this is an ACK packet, otherwise this packet has payload.
|
||||
IPseudoTcpNotify::WriteResult packet(uint32_t seq,
|
||||
uint8_t flags,
|
||||
uint32_t offset,
|
||||
uint32_t len);
|
||||
bool parse(const uint8_t* buffer, uint32_t size);
|
||||
|
||||
void attemptSend(SendFlags sflags = sfNone);
|
||||
|
||||
void closedown(uint32_t err = 0);
|
||||
|
||||
bool clock_check(uint32_t now, long& nTimeout);
|
||||
|
||||
bool process(Segment& seg);
|
||||
bool transmit(const SList::iterator& seg, uint32_t now);
|
||||
|
||||
void adjustMTU();
|
||||
|
||||
protected:
|
||||
// This method is used in test only to query receive buffer state.
|
||||
bool isReceiveBufferFull() const;
|
||||
|
||||
// This method is only used in tests, to disable window scaling
|
||||
// support for testing backward compatibility.
|
||||
void disableWindowScale();
|
||||
|
||||
private:
|
||||
// Queue the connect message with TCP options.
|
||||
void queueConnectMessage();
|
||||
|
||||
// Parse TCP options in the header.
|
||||
void parseOptions(const char* data, uint32_t len);
|
||||
|
||||
// Apply a TCP option that has been read from the header.
|
||||
void applyOption(char kind, const char* data, uint32_t len);
|
||||
|
||||
// Apply window scale option.
|
||||
void applyWindowScaleOption(uint8_t scale_factor);
|
||||
|
||||
// Resize the send buffer with `new_size` in bytes.
|
||||
void resizeSendBuffer(uint32_t new_size);
|
||||
|
||||
// Resize the receive buffer with `new_size` in bytes. This call adjusts
|
||||
// window scale factor `m_swnd_scale` accordingly.
|
||||
void resizeReceiveBuffer(uint32_t new_size);
|
||||
|
||||
class LockedFifoBuffer final {
|
||||
public:
|
||||
explicit LockedFifoBuffer(size_t size);
|
||||
~LockedFifoBuffer();
|
||||
|
||||
size_t GetBuffered() const;
|
||||
bool SetCapacity(size_t size);
|
||||
bool ReadOffset(void* buffer,
|
||||
size_t bytes,
|
||||
size_t offset,
|
||||
size_t* bytes_read);
|
||||
bool WriteOffset(const void* buffer,
|
||||
size_t bytes,
|
||||
size_t offset,
|
||||
size_t* bytes_written);
|
||||
bool Read(void* buffer, size_t bytes, size_t* bytes_read);
|
||||
bool Write(const void* buffer, size_t bytes, size_t* bytes_written);
|
||||
void ConsumeReadData(size_t size);
|
||||
void ConsumeWriteBuffer(size_t size);
|
||||
bool GetWriteRemaining(size_t* size) const;
|
||||
|
||||
private:
|
||||
bool ReadOffsetLocked(void* buffer,
|
||||
size_t bytes,
|
||||
size_t offset,
|
||||
size_t* bytes_read)
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
bool WriteOffsetLocked(const void* buffer,
|
||||
size_t bytes,
|
||||
size_t offset,
|
||||
size_t* bytes_written)
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// the allocated buffer
|
||||
std::unique_ptr<char[]> buffer_ RTC_GUARDED_BY(mutex_);
|
||||
// size of the allocated buffer
|
||||
size_t buffer_length_ RTC_GUARDED_BY(mutex_);
|
||||
// amount of readable data in the buffer
|
||||
size_t data_length_ RTC_GUARDED_BY(mutex_);
|
||||
// offset to the readable data
|
||||
size_t read_position_ RTC_GUARDED_BY(mutex_);
|
||||
mutable webrtc::Mutex mutex_;
|
||||
};
|
||||
|
||||
IPseudoTcpNotify* m_notify;
|
||||
enum Shutdown { SD_NONE, SD_GRACEFUL, SD_FORCEFUL } m_shutdown;
|
||||
int m_error;
|
||||
|
||||
// TCB data
|
||||
TcpState m_state;
|
||||
uint32_t m_conv;
|
||||
bool m_bReadEnable, m_bWriteEnable, m_bOutgoing;
|
||||
uint32_t m_lasttraffic;
|
||||
|
||||
// Incoming data
|
||||
typedef std::list<RSegment> RList;
|
||||
RList m_rlist;
|
||||
uint32_t m_rbuf_len, m_rcv_nxt, m_rcv_wnd, m_lastrecv;
|
||||
uint8_t m_rwnd_scale; // Window scale factor.
|
||||
LockedFifoBuffer m_rbuf;
|
||||
|
||||
// Outgoing data
|
||||
SList m_slist;
|
||||
uint32_t m_sbuf_len, m_snd_nxt, m_snd_wnd, m_lastsend, m_snd_una;
|
||||
uint8_t m_swnd_scale; // Window scale factor.
|
||||
LockedFifoBuffer m_sbuf;
|
||||
|
||||
// Maximum segment size, estimated protocol level, largest segment sent
|
||||
uint32_t m_mss, m_msslevel, m_largest, m_mtu_advise;
|
||||
// Retransmit timer
|
||||
uint32_t m_rto_base;
|
||||
|
||||
// Timestamp tracking
|
||||
uint32_t m_ts_recent, m_ts_lastack;
|
||||
|
||||
// Round-trip calculation
|
||||
uint32_t m_rx_rttvar, m_rx_srtt, m_rx_rto;
|
||||
|
||||
// Congestion avoidance, Fast retransmit/recovery, Delayed ACKs
|
||||
uint32_t m_ssthresh, m_cwnd;
|
||||
uint8_t m_dup_acks;
|
||||
uint32_t m_recover;
|
||||
uint32_t m_t_ack;
|
||||
|
||||
// Configuration options
|
||||
bool m_use_nagling;
|
||||
uint32_t m_ack_delay;
|
||||
|
||||
// This is used by unit tests to test backward compatibility of
|
||||
// PseudoTcp implementations that don't support window scaling.
|
||||
bool m_support_wnd_scale;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_PSEUDO_TCP_H_
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/regathering_controller.h"
|
||||
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/units/time_delta.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
BasicRegatheringController::BasicRegatheringController(
|
||||
const Config& config,
|
||||
cricket::IceTransportInternal* ice_transport,
|
||||
rtc::Thread* thread)
|
||||
: config_(config), ice_transport_(ice_transport), thread_(thread) {
|
||||
RTC_DCHECK(thread_);
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
RTC_DCHECK(ice_transport_);
|
||||
ice_transport_->SignalStateChanged.connect(
|
||||
this, &BasicRegatheringController::OnIceTransportStateChanged);
|
||||
ice_transport->SignalWritableState.connect(
|
||||
this, &BasicRegatheringController::OnIceTransportWritableState);
|
||||
ice_transport->SignalReceivingState.connect(
|
||||
this, &BasicRegatheringController::OnIceTransportReceivingState);
|
||||
ice_transport->SignalNetworkRouteChanged.connect(
|
||||
this, &BasicRegatheringController::OnIceTransportNetworkRouteChanged);
|
||||
}
|
||||
|
||||
BasicRegatheringController::~BasicRegatheringController() {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
}
|
||||
|
||||
void BasicRegatheringController::Start() {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
ScheduleRecurringRegatheringOnFailedNetworks();
|
||||
}
|
||||
|
||||
void BasicRegatheringController::SetConfig(const Config& config) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
bool need_reschedule_on_failed_networks =
|
||||
pending_regathering_ && (config_.regather_on_failed_networks_interval !=
|
||||
config.regather_on_failed_networks_interval);
|
||||
config_ = config;
|
||||
if (need_reschedule_on_failed_networks) {
|
||||
ScheduleRecurringRegatheringOnFailedNetworks();
|
||||
}
|
||||
}
|
||||
|
||||
void BasicRegatheringController::
|
||||
ScheduleRecurringRegatheringOnFailedNetworks() {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
RTC_DCHECK(config_.regather_on_failed_networks_interval >= 0);
|
||||
// Reset pending_regathering_ to cancel any potentially pending tasks.
|
||||
pending_regathering_.reset(new ScopedTaskSafety());
|
||||
|
||||
thread_->PostDelayedTask(
|
||||
SafeTask(pending_regathering_->flag(),
|
||||
[this]() {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
// Only regather when the current session is in the CLEARED
|
||||
// state (i.e., not running or stopped). It is only
|
||||
// possible to enter this state when we gather continually,
|
||||
// so there is an implicit check on continual gathering
|
||||
// here.
|
||||
if (allocator_session_ && allocator_session_->IsCleared()) {
|
||||
allocator_session_->RegatherOnFailedNetworks();
|
||||
}
|
||||
ScheduleRecurringRegatheringOnFailedNetworks();
|
||||
}),
|
||||
TimeDelta::Millis(config_.regather_on_failed_networks_interval));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_REGATHERING_CONTROLLER_H_
|
||||
#define P2P_BASE_REGATHERING_CONTROLLER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "rtc_base/thread.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Controls regathering of candidates for the ICE transport passed into it,
|
||||
// reacting to signals like SignalWritableState, SignalNetworkRouteChange, etc.,
|
||||
// using methods like GetStats to get additional information, and calling
|
||||
// methods like RegatherOnFailedNetworks on the PortAllocatorSession when
|
||||
// regathering is desired.
|
||||
//
|
||||
// "Regathering" is defined as gathering additional candidates within a single
|
||||
// ICE generation (or in other words, PortAllocatorSession), and is possible
|
||||
// when "continual gathering" is enabled. This may allow connectivity to be
|
||||
// maintained and/or restored without a full ICE restart.
|
||||
//
|
||||
// Regathering will only begin after PortAllocationSession is set via
|
||||
// set_allocator_session. This should be called any time the "active"
|
||||
// PortAllocatorSession is changed (in other words, when an ICE restart occurs),
|
||||
// so that candidates are gathered for the "current" ICE generation.
|
||||
//
|
||||
// All methods of BasicRegatheringController should be called on the same
|
||||
// thread as the one passed to the constructor, and this thread should be the
|
||||
// same one where PortAllocatorSession runs, which is also identical to the
|
||||
// network thread of the ICE transport, as given by
|
||||
// P2PTransportChannel::thread().
|
||||
class BasicRegatheringController : public sigslot::has_slots<> {
|
||||
public:
|
||||
struct Config {
|
||||
int regather_on_failed_networks_interval =
|
||||
cricket::REGATHER_ON_FAILED_NETWORKS_INTERVAL;
|
||||
};
|
||||
|
||||
BasicRegatheringController() = delete;
|
||||
BasicRegatheringController(const Config& config,
|
||||
cricket::IceTransportInternal* ice_transport,
|
||||
rtc::Thread* thread);
|
||||
~BasicRegatheringController() override;
|
||||
// TODO(qingsi): Remove this method after implementing a new signal in
|
||||
// P2PTransportChannel and reacting to that signal for the initial schedules
|
||||
// of regathering.
|
||||
void Start();
|
||||
void set_allocator_session(cricket::PortAllocatorSession* allocator_session) {
|
||||
allocator_session_ = allocator_session;
|
||||
}
|
||||
// Setting a different config of the regathering interval range on all
|
||||
// networks cancels and reschedules the recurring schedules, if any, of
|
||||
// regathering on all networks. The same applies to the change of the
|
||||
// regathering interval on the failed networks. This rescheduling behavior is
|
||||
// seperately defined for the two config parameters.
|
||||
void SetConfig(const Config& config);
|
||||
|
||||
private:
|
||||
// TODO(qingsi): Implement the following methods and use methods from the ICE
|
||||
// transport like GetStats to get additional information for the decision
|
||||
// making in regathering.
|
||||
void OnIceTransportStateChanged(cricket::IceTransportInternal*) {}
|
||||
void OnIceTransportWritableState(rtc::PacketTransportInternal*) {}
|
||||
void OnIceTransportReceivingState(rtc::PacketTransportInternal*) {}
|
||||
void OnIceTransportNetworkRouteChanged(absl::optional<rtc::NetworkRoute>) {}
|
||||
// Schedules delayed and repeated regathering of local candidates on failed
|
||||
// networks, where the delay in milliseconds is given by the config. Each
|
||||
// repetition is separated by the same delay. When scheduled, all previous
|
||||
// schedules are canceled.
|
||||
void ScheduleRecurringRegatheringOnFailedNetworks();
|
||||
// Cancels regathering scheduled by ScheduleRecurringRegatheringOnAllNetworks.
|
||||
void CancelScheduledRecurringRegatheringOnAllNetworks();
|
||||
|
||||
// We use a flag to be able to cancel pending regathering operations when
|
||||
// the object goes out of scope or the config changes.
|
||||
std::unique_ptr<ScopedTaskSafety> pending_regathering_;
|
||||
Config config_;
|
||||
cricket::IceTransportInternal* ice_transport_;
|
||||
cricket::PortAllocatorSession* allocator_session_ = nullptr;
|
||||
rtc::Thread* const thread_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // P2P_BASE_REGATHERING_CONTROLLER_H_
|
||||
358
TMessagesProj/jni/voip/webrtc/p2p/base/stun_dictionary.cc
Normal file
358
TMessagesProj/jni/voip/webrtc/p2p/base/stun_dictionary.cc
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/stun_dictionary.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <utility>
|
||||
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
const StunAddressAttribute* StunDictionaryView::GetAddress(int key) const {
|
||||
const StunAttribute* attr = GetOrNull(key, STUN_VALUE_ADDRESS);
|
||||
if (attr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<const StunAddressAttribute*>(attr);
|
||||
}
|
||||
|
||||
const StunUInt32Attribute* StunDictionaryView::GetUInt32(int key) const {
|
||||
const StunAttribute* attr = GetOrNull(key, STUN_VALUE_UINT32);
|
||||
if (attr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<const StunUInt32Attribute*>(attr);
|
||||
}
|
||||
|
||||
const StunUInt64Attribute* StunDictionaryView::GetUInt64(int key) const {
|
||||
const StunAttribute* attr = GetOrNull(key, STUN_VALUE_UINT64);
|
||||
if (attr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<const StunUInt64Attribute*>(attr);
|
||||
}
|
||||
|
||||
const StunByteStringAttribute* StunDictionaryView::GetByteString(
|
||||
int key) const {
|
||||
const StunAttribute* attr = GetOrNull(key, STUN_VALUE_BYTE_STRING);
|
||||
if (attr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<const StunByteStringAttribute*>(attr);
|
||||
}
|
||||
|
||||
const StunUInt16ListAttribute* StunDictionaryView::GetUInt16List(
|
||||
int key) const {
|
||||
const StunAttribute* attr = GetOrNull(key, STUN_VALUE_UINT16_LIST);
|
||||
if (attr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<const StunUInt16ListAttribute*>(attr);
|
||||
}
|
||||
|
||||
const StunAttribute* StunDictionaryView::GetOrNull(
|
||||
int key,
|
||||
absl::optional<StunAttributeValueType> type) const {
|
||||
const auto it = attrs_.find(key);
|
||||
if (it == attrs_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (type && it->second->value_type() != *type) {
|
||||
RTC_LOG(LS_WARNING) << "Get key: " << key << " with type: " << *type
|
||||
<< " found different type: "
|
||||
<< it->second->value_type();
|
||||
return nullptr;
|
||||
}
|
||||
return (*it).second.get();
|
||||
}
|
||||
|
||||
webrtc::RTCErrorOr<
|
||||
std::pair<uint64_t, std::deque<std::unique_ptr<StunAttribute>>>>
|
||||
StunDictionaryView::ParseDelta(const StunByteStringAttribute& delta) {
|
||||
rtc::ByteBufferReader buf(delta.array_view());
|
||||
uint16_t magic;
|
||||
if (!buf.ReadUInt16(&magic)) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to read magic number");
|
||||
}
|
||||
if (magic != kDeltaMagic) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Invalid magic number");
|
||||
}
|
||||
|
||||
uint16_t delta_version;
|
||||
if (!buf.ReadUInt16(&delta_version)) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to read version");
|
||||
}
|
||||
|
||||
if (delta_version != kDeltaVersion) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Unsupported delta version");
|
||||
}
|
||||
|
||||
// Now read all the attributes
|
||||
std::deque<std::unique_ptr<StunAttribute>> attrs;
|
||||
while (buf.Length()) {
|
||||
uint16_t key, length, value_type;
|
||||
if (!buf.ReadUInt16(&key)) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to read attribute key");
|
||||
}
|
||||
if (!buf.ReadUInt16(&length)) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to read attribute length");
|
||||
}
|
||||
if (!buf.ReadUInt16(&value_type)) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to read value type");
|
||||
}
|
||||
|
||||
StunAttributeValueType value_type_enum =
|
||||
static_cast<StunAttributeValueType>(value_type);
|
||||
std::unique_ptr<StunAttribute> attr(
|
||||
StunAttribute::Create(value_type_enum, key, length, nullptr));
|
||||
if (!attr) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to create attribute");
|
||||
}
|
||||
if (attr->length() != length) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Inconsistent attribute length");
|
||||
}
|
||||
if (!attr->Read(&buf)) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Failed to read attribute content");
|
||||
}
|
||||
attrs.push_back(std::move(attr));
|
||||
}
|
||||
|
||||
// The first attribute should be the version...
|
||||
if (attrs.empty()) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Empty delta!");
|
||||
}
|
||||
|
||||
if (attrs[0]->type() != kVersionKey ||
|
||||
attrs[0]->value_type() != STUN_VALUE_UINT64) {
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
|
||||
"Missing version!");
|
||||
}
|
||||
|
||||
uint64_t version_in_delta =
|
||||
reinterpret_cast<const StunUInt64Attribute*>(attrs[0].get())->value();
|
||||
attrs.pop_front();
|
||||
|
||||
return std::make_pair(std::max(version_in_delta, version_in_delta),
|
||||
std::move(attrs));
|
||||
}
|
||||
|
||||
// Apply a delta return an StunUInt64Attribute to ack the update.
|
||||
webrtc::RTCErrorOr<
|
||||
std::pair<std::unique_ptr<StunUInt64Attribute>, std::vector<uint16_t>>>
|
||||
StunDictionaryView::ApplyDelta(const StunByteStringAttribute& delta) {
|
||||
auto parsed_delta = ParseDelta(delta);
|
||||
if (!parsed_delta.ok()) {
|
||||
return webrtc::RTCError(parsed_delta.error());
|
||||
}
|
||||
|
||||
uint64_t version_in_delta = parsed_delta.value().first;
|
||||
|
||||
// Check that update does not overflow max_bytes_stored_.
|
||||
int new_bytes_stored = bytes_stored_;
|
||||
for (auto& attr : parsed_delta.value().second) {
|
||||
auto old_version = version_per_key_.find(attr->type());
|
||||
if (old_version == version_per_key_.end() ||
|
||||
version_in_delta > old_version->second) {
|
||||
size_t new_length = attr->length();
|
||||
size_t old_length = GetLength(attr->type());
|
||||
if (old_version == version_per_key_.end()) {
|
||||
new_length += sizeof(int64_t);
|
||||
}
|
||||
|
||||
new_bytes_stored = new_bytes_stored + new_length - old_length;
|
||||
if (new_bytes_stored <= 0) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "attr: " << attr->type() << " old_length: " << old_length
|
||||
<< " new_length: " << new_length
|
||||
<< " bytes_stored_: " << bytes_stored_
|
||||
<< " new_bytes_stored: " << new_bytes_stored;
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER);
|
||||
}
|
||||
if (new_bytes_stored > max_bytes_stored_) {
|
||||
RTC_LOG(LS_INFO) << "attr: " << attr->type()
|
||||
<< " old_length: " << old_length
|
||||
<< " new_length: " << new_length
|
||||
<< " bytes_stored_: " << bytes_stored_
|
||||
<< " new_bytes_stored: " << new_bytes_stored;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (new_bytes_stored > max_bytes_stored_) {
|
||||
RTC_LOG(LS_INFO) << " bytes_stored_: " << bytes_stored_
|
||||
<< " new_bytes_stored: " << new_bytes_stored;
|
||||
return webrtc::RTCError(webrtc::RTCErrorType::RESOURCE_EXHAUSTED);
|
||||
}
|
||||
|
||||
// Apply the update.
|
||||
std::vector<uint16_t> keys;
|
||||
for (auto& attr : parsed_delta.value().second) {
|
||||
if (version_in_delta > version_per_key_[attr->type()]) {
|
||||
version_per_key_[attr->type()] = version_in_delta;
|
||||
keys.push_back(attr->type());
|
||||
if (attr->value_type() == STUN_VALUE_BYTE_STRING && attr->length() == 0) {
|
||||
attrs_.erase(attr->type());
|
||||
} else {
|
||||
int attribute_type = attr->type();
|
||||
attrs_[attribute_type] = std::move(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
bytes_stored_ = new_bytes_stored;
|
||||
|
||||
return std::make_pair(std::make_unique<StunUInt64Attribute>(
|
||||
STUN_ATTR_GOOG_DELTA_ACK, version_in_delta),
|
||||
std::move(keys));
|
||||
}
|
||||
|
||||
size_t StunDictionaryView::GetLength(int key) const {
|
||||
auto attr = GetOrNull(key);
|
||||
if (attr != nullptr) {
|
||||
return attr->length();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StunDictionaryWriter::Disable() {
|
||||
disabled_ = true;
|
||||
}
|
||||
|
||||
void StunDictionaryWriter::Delete(int key) {
|
||||
if (disabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dictionary_) {
|
||||
if (dictionary_->attrs_.find(key) == dictionary_->attrs_.end()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// remove any pending updates.
|
||||
pending_.erase(
|
||||
std::remove_if(pending_.begin(), pending_.end(),
|
||||
[key](const auto& p) { return p.second->type() == key; }),
|
||||
pending_.end());
|
||||
|
||||
// Create tombstone.
|
||||
auto tombstone = std::make_unique<StunByteStringAttribute>(key);
|
||||
|
||||
// add a pending entry.
|
||||
pending_.push_back(std::make_pair(++version_, tombstone.get()));
|
||||
|
||||
// store the tombstone.
|
||||
tombstones_[key] = std::move(tombstone);
|
||||
|
||||
if (dictionary_) {
|
||||
// remove value
|
||||
dictionary_->attrs_.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
void StunDictionaryWriter::Set(std::unique_ptr<StunAttribute> attr) {
|
||||
if (disabled_) {
|
||||
return;
|
||||
}
|
||||
int key = attr->type();
|
||||
// remove any pending updates.
|
||||
pending_.erase(
|
||||
std::remove_if(pending_.begin(), pending_.end(),
|
||||
[key](const auto& p) { return p.second->type() == key; }),
|
||||
pending_.end());
|
||||
|
||||
// remove any existing key.
|
||||
tombstones_.erase(key);
|
||||
|
||||
// create pending entry.
|
||||
pending_.push_back(std::make_pair(++version_, attr.get()));
|
||||
|
||||
if (dictionary_) {
|
||||
// store attribute.
|
||||
dictionary_->attrs_[key] = std::move(attr);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an StunByteStringAttribute containing the pending (e.g not ack:ed)
|
||||
// modifications.
|
||||
std::unique_ptr<StunByteStringAttribute> StunDictionaryWriter::CreateDelta() {
|
||||
if (disabled_) {
|
||||
return nullptr;
|
||||
}
|
||||
if (pending_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rtc::ByteBufferWriter buf;
|
||||
buf.WriteUInt16(StunDictionaryView::kDeltaMagic); // 0,1
|
||||
buf.WriteUInt16(StunDictionaryView::kDeltaVersion); // 2,3
|
||||
|
||||
// max version in Delta.
|
||||
buf.WriteUInt16(StunDictionaryView::kVersionKey); // 4,5
|
||||
buf.WriteUInt16(8); // 6,7
|
||||
buf.WriteUInt16(STUN_VALUE_UINT64); // 8,9
|
||||
buf.WriteUInt64(pending_.back().first); // 10-17
|
||||
// attributes
|
||||
for (const auto& attr : pending_) {
|
||||
buf.WriteUInt16(attr.second->type());
|
||||
buf.WriteUInt16(static_cast<uint16_t>(attr.second->length()));
|
||||
buf.WriteUInt16(attr.second->value_type());
|
||||
if (!attr.second->Write(&buf)) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to write key: " << attr.second->type();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return std::make_unique<StunByteStringAttribute>(STUN_ATTR_GOOG_DELTA,
|
||||
buf.Data(), buf.Length());
|
||||
}
|
||||
|
||||
// Apply a delta ack, i.e prune list of pending changes.
|
||||
void StunDictionaryWriter::ApplyDeltaAck(const StunUInt64Attribute& ack) {
|
||||
uint64_t acked_version = ack.value();
|
||||
auto entries_to_remove = std::remove_if(
|
||||
pending_.begin(), pending_.end(),
|
||||
[acked_version](const auto& p) { return p.first <= acked_version; });
|
||||
|
||||
// remove tombstones.
|
||||
for (auto it = entries_to_remove; it != pending_.end(); ++it) {
|
||||
tombstones_.erase((*it).second->type());
|
||||
}
|
||||
pending_.erase(entries_to_remove, pending_.end());
|
||||
}
|
||||
|
||||
// Check if a key has a pending change (i.e a change
|
||||
// that has not been acked).
|
||||
bool StunDictionaryWriter::Pending(int key) const {
|
||||
for (const auto& attr : pending_) {
|
||||
if (attr.second->type() == key) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int StunDictionaryWriter::Pending() const {
|
||||
return pending_.size();
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
204
TMessagesProj/jni/voip/webrtc/p2p/base/stun_dictionary.h
Normal file
204
TMessagesProj/jni/voip/webrtc/p2p/base/stun_dictionary.h
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_STUN_DICTIONARY_H_
|
||||
#define P2P_BASE_STUN_DICTIONARY_H_
|
||||
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/rtc_error.h"
|
||||
#include "api/transport/stun.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// A StunDictionaryView is a dictionary of StunAttributes.
|
||||
// - the StunAttributes can be read using the |Get|-methods.
|
||||
// - the dictionary is updated by using the |ApplyDelta|-method.
|
||||
//
|
||||
// A StunDictionaryWriter is used to create |delta|s for the |ApplyDelta|-method
|
||||
// - It keeps track of which updates has been applied at StunDictionaryView.
|
||||
// - It optionally keeps a local StunDictionaryView contains modification made
|
||||
// `locally`
|
||||
//
|
||||
// A pair StunDictionaryView(A)/StunDictionaryWriter(B) are linked so that
|
||||
// modifications to B is transfered to A using the STUN_ATTR_GOOG_DELTA
|
||||
// (StunByteStringAttribute) and the modification is ack:ed using
|
||||
// STUN_ATTR_GOOG_DELTA_ACK (StunUInt64Attribute).
|
||||
//
|
||||
// Note:
|
||||
// 1) It is possible to update one StunDictionaryView from multiple writers,
|
||||
// but this only works of the different writers write disjoint keys (which
|
||||
// is not checked/enforced by these classes).
|
||||
// 2) The opposite, one writer updating multiple StunDictionaryView, is not
|
||||
// possible.
|
||||
class StunDictionaryView {
|
||||
public:
|
||||
// A reserved key used to transport the version number
|
||||
static constexpr uint16_t kVersionKey = 0xFFFF;
|
||||
|
||||
// A magic number used when transporting deltas.
|
||||
static constexpr uint16_t kDeltaMagic = 0x7788;
|
||||
|
||||
// The version number for the delta format.
|
||||
static constexpr uint16_t kDeltaVersion = 0x1;
|
||||
|
||||
// Gets the desired attribute value, or NULL if no such attribute type exists.
|
||||
// The pointer returned is guaranteed to be valid until ApplyDelta is called.
|
||||
const StunAddressAttribute* GetAddress(int key) const;
|
||||
const StunUInt32Attribute* GetUInt32(int key) const;
|
||||
const StunUInt64Attribute* GetUInt64(int key) const;
|
||||
const StunByteStringAttribute* GetByteString(int key) const;
|
||||
const StunUInt16ListAttribute* GetUInt16List(int key) const;
|
||||
|
||||
bool empty() const { return attrs_.empty(); }
|
||||
size_t size() const { return attrs_.size(); }
|
||||
int bytes_stored() const { return bytes_stored_; }
|
||||
void set_max_bytes_stored(int max_bytes_stored) {
|
||||
max_bytes_stored_ = max_bytes_stored;
|
||||
}
|
||||
|
||||
// Apply a delta and return
|
||||
// a pair with
|
||||
// - StunUInt64Attribute to ack the |delta|.
|
||||
// - vector of keys that was modified.
|
||||
webrtc::RTCErrorOr<
|
||||
std::pair<std::unique_ptr<StunUInt64Attribute>, std::vector<uint16_t>>>
|
||||
ApplyDelta(const StunByteStringAttribute& delta);
|
||||
|
||||
private:
|
||||
friend class StunDictionaryWriter;
|
||||
|
||||
const StunAttribute* GetOrNull(
|
||||
int key,
|
||||
absl::optional<StunAttributeValueType> = absl::nullopt) const;
|
||||
size_t GetLength(int key) const;
|
||||
static webrtc::RTCErrorOr<
|
||||
std::pair<uint64_t, std::deque<std::unique_ptr<StunAttribute>>>>
|
||||
ParseDelta(const StunByteStringAttribute& delta);
|
||||
|
||||
std::map<uint16_t, std::unique_ptr<StunAttribute>> attrs_;
|
||||
std::map<uint16_t, uint64_t> version_per_key_;
|
||||
|
||||
int max_bytes_stored_ = 16384;
|
||||
int bytes_stored_ = 0;
|
||||
};
|
||||
|
||||
class StunDictionaryWriter {
|
||||
public:
|
||||
StunDictionaryWriter() {
|
||||
dictionary_ = std::make_unique<StunDictionaryView>();
|
||||
}
|
||||
explicit StunDictionaryWriter(
|
||||
std::unique_ptr<StunDictionaryView> dictionary) {
|
||||
dictionary_ = std::move(dictionary);
|
||||
}
|
||||
|
||||
// A pending modification.
|
||||
template <typename T>
|
||||
class Modification {
|
||||
public:
|
||||
~Modification() { commit(); }
|
||||
|
||||
T* operator->() { return attr_.get(); }
|
||||
|
||||
void abort() { attr_ = nullptr; }
|
||||
void commit() {
|
||||
if (attr_) {
|
||||
writer_->Set(std::move(attr_));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
friend class StunDictionaryWriter;
|
||||
Modification(StunDictionaryWriter* writer, std::unique_ptr<T> attr)
|
||||
: writer_(writer), attr_(std::move(attr)) {}
|
||||
StunDictionaryWriter* writer_;
|
||||
std::unique_ptr<T> attr_;
|
||||
|
||||
Modification(const Modification<T>&) =
|
||||
delete; // not copyable (but movable).
|
||||
Modification& operator=(Modification<T>&) =
|
||||
delete; // not copyable (but movable).
|
||||
};
|
||||
|
||||
// Record a modification.
|
||||
Modification<StunAddressAttribute> SetAddress(int key) {
|
||||
return Modification<StunAddressAttribute>(
|
||||
this, StunAttribute::CreateAddress(key));
|
||||
}
|
||||
Modification<StunUInt32Attribute> SetUInt32(int key) {
|
||||
return Modification<StunUInt32Attribute>(this,
|
||||
StunAttribute::CreateUInt32(key));
|
||||
}
|
||||
Modification<StunUInt64Attribute> SetUInt64(int key) {
|
||||
return Modification<StunUInt64Attribute>(this,
|
||||
StunAttribute::CreateUInt64(key));
|
||||
}
|
||||
Modification<StunByteStringAttribute> SetByteString(int key) {
|
||||
return Modification<StunByteStringAttribute>(
|
||||
this, StunAttribute::CreateByteString(key));
|
||||
}
|
||||
Modification<StunUInt16ListAttribute> SetUInt16List(int key) {
|
||||
return Modification<StunUInt16ListAttribute>(
|
||||
this, StunAttribute::CreateUInt16ListAttribute(key));
|
||||
}
|
||||
|
||||
// Delete a key.
|
||||
void Delete(int key);
|
||||
|
||||
// Check if a key has a pending change (i.e a change
|
||||
// that has not been acked).
|
||||
bool Pending(int key) const;
|
||||
|
||||
// Return number of of pending modifications.
|
||||
int Pending() const;
|
||||
|
||||
// Create an StunByteStringAttribute containing the pending (e.g not ack:ed)
|
||||
// modifications.
|
||||
std::unique_ptr<StunByteStringAttribute> CreateDelta();
|
||||
|
||||
// Apply an delta ack.
|
||||
void ApplyDeltaAck(const StunUInt64Attribute&);
|
||||
|
||||
// Return pointer to (optional) StunDictionaryView.
|
||||
const StunDictionaryView* dictionary() { return dictionary_.get(); }
|
||||
const StunDictionaryView* operator->() { return dictionary_.get(); }
|
||||
|
||||
// Disable writer,
|
||||
// i.e CreateDelta always return null, and no modifications are made.
|
||||
// This is called if remote peer does not support GOOG_DELTA.
|
||||
void Disable();
|
||||
bool disabled() const { return disabled_; }
|
||||
|
||||
private:
|
||||
void Set(std::unique_ptr<StunAttribute> attr);
|
||||
|
||||
bool disabled_ = false;
|
||||
|
||||
// version of modification.
|
||||
int64_t version_ = 1;
|
||||
|
||||
// (optional) StunDictionaryView.
|
||||
std::unique_ptr<StunDictionaryView> dictionary_;
|
||||
|
||||
// sorted list of changes that has not been yet been ack:ed.
|
||||
std::vector<std::pair<uint64_t, const StunAttribute*>> pending_;
|
||||
|
||||
// tombstones, i.e values that has been deleted but not yet acked.
|
||||
std::map<uint16_t, std::unique_ptr<StunAttribute>> tombstones_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_STUN_DICTIONARY_H_
|
||||
673
TMessagesProj/jni/voip/webrtc/p2p/base/stun_port.cc
Normal file
673
TMessagesProj/jni/voip/webrtc/p2p/base/stun_port.cc
Normal file
|
|
@ -0,0 +1,673 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/stun_port.h"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/transport/stun.h"
|
||||
#include "p2p/base/connection.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/experiments/field_trial_parser.h"
|
||||
#include "rtc_base/helpers.h"
|
||||
#include "rtc_base/ip_address.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/network/received_packet.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// TODO(?): Move these to a common place (used in relayport too)
|
||||
const int RETRY_TIMEOUT = 50 * 1000; // 50 seconds
|
||||
|
||||
// Stop logging errors in UDPPort::SendTo after we have logged
|
||||
// `kSendErrorLogLimit` messages. Start again after a successful send.
|
||||
const int kSendErrorLogLimit = 5;
|
||||
|
||||
// Handles a binding request sent to the STUN server.
|
||||
class StunBindingRequest : public StunRequest {
|
||||
public:
|
||||
StunBindingRequest(UDPPort* port,
|
||||
const rtc::SocketAddress& addr,
|
||||
int64_t start_time)
|
||||
: StunRequest(port->request_manager(),
|
||||
std::make_unique<StunMessage>(STUN_BINDING_REQUEST)),
|
||||
port_(port),
|
||||
server_addr_(addr),
|
||||
start_time_(start_time) {
|
||||
SetAuthenticationRequired(false);
|
||||
}
|
||||
|
||||
const rtc::SocketAddress& server_addr() const { return server_addr_; }
|
||||
|
||||
void OnResponse(StunMessage* response) override {
|
||||
const StunAddressAttribute* addr_attr =
|
||||
response->GetAddress(STUN_ATTR_MAPPED_ADDRESS);
|
||||
if (!addr_attr) {
|
||||
RTC_LOG(LS_ERROR) << "Binding response missing mapped address.";
|
||||
} else if (addr_attr->family() != STUN_ADDRESS_IPV4 &&
|
||||
addr_attr->family() != STUN_ADDRESS_IPV6) {
|
||||
RTC_LOG(LS_ERROR) << "Binding address has bad family";
|
||||
} else {
|
||||
rtc::SocketAddress addr(addr_attr->ipaddr(), addr_attr->port());
|
||||
port_->OnStunBindingRequestSucceeded(this->Elapsed(), server_addr_, addr);
|
||||
}
|
||||
|
||||
// The keep-alive requests will be stopped after its lifetime has passed.
|
||||
if (WithinLifetime(rtc::TimeMillis())) {
|
||||
port_->request_manager_.SendDelayed(
|
||||
new StunBindingRequest(port_, server_addr_, start_time_),
|
||||
port_->stun_keepalive_delay());
|
||||
}
|
||||
}
|
||||
|
||||
void OnErrorResponse(StunMessage* response) override {
|
||||
const StunErrorCodeAttribute* attr = response->GetErrorCode();
|
||||
if (!attr) {
|
||||
RTC_LOG(LS_ERROR) << "Missing binding response error code.";
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Binding error response:"
|
||||
" class="
|
||||
<< attr->eclass() << " number=" << attr->number()
|
||||
<< " reason=" << attr->reason();
|
||||
}
|
||||
|
||||
port_->OnStunBindingOrResolveRequestFailed(
|
||||
server_addr_, attr ? attr->number() : STUN_ERROR_GLOBAL_FAILURE,
|
||||
attr ? attr->reason()
|
||||
: "STUN binding response with no error code attribute.");
|
||||
|
||||
int64_t now = rtc::TimeMillis();
|
||||
if (WithinLifetime(now) &&
|
||||
rtc::TimeDiff(now, start_time_) < RETRY_TIMEOUT) {
|
||||
port_->request_manager_.SendDelayed(
|
||||
new StunBindingRequest(port_, server_addr_, start_time_),
|
||||
port_->stun_keepalive_delay());
|
||||
}
|
||||
}
|
||||
void OnTimeout() override {
|
||||
RTC_LOG(LS_ERROR) << "Binding request timed out from "
|
||||
<< port_->GetLocalAddress().ToSensitiveString() << " ("
|
||||
<< port_->Network()->name() << ")";
|
||||
port_->OnStunBindingOrResolveRequestFailed(
|
||||
server_addr_, SERVER_NOT_REACHABLE_ERROR,
|
||||
"STUN binding request timed out.");
|
||||
}
|
||||
|
||||
private:
|
||||
// Returns true if `now` is within the lifetime of the request (a negative
|
||||
// lifetime means infinite).
|
||||
bool WithinLifetime(int64_t now) const {
|
||||
int lifetime = port_->stun_keepalive_lifetime();
|
||||
return lifetime < 0 || rtc::TimeDiff(now, start_time_) <= lifetime;
|
||||
}
|
||||
|
||||
UDPPort* port_;
|
||||
const rtc::SocketAddress server_addr_;
|
||||
|
||||
int64_t start_time_;
|
||||
};
|
||||
|
||||
UDPPort::AddressResolver::AddressResolver(
|
||||
rtc::PacketSocketFactory* factory,
|
||||
std::function<void(const rtc::SocketAddress&, int)> done_callback)
|
||||
: socket_factory_(factory), done_(std::move(done_callback)) {}
|
||||
|
||||
void UDPPort::AddressResolver::Resolve(
|
||||
const rtc::SocketAddress& address,
|
||||
int family,
|
||||
const webrtc::FieldTrialsView& field_trials) {
|
||||
if (resolvers_.find(address) != resolvers_.end())
|
||||
return;
|
||||
|
||||
auto resolver = socket_factory_->CreateAsyncDnsResolver();
|
||||
auto resolver_ptr = resolver.get();
|
||||
std::pair<rtc::SocketAddress,
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface>>
|
||||
pair = std::make_pair(address, std::move(resolver));
|
||||
|
||||
resolvers_.insert(std::move(pair));
|
||||
auto callback = [this, address] {
|
||||
ResolverMap::const_iterator it = resolvers_.find(address);
|
||||
if (it != resolvers_.end()) {
|
||||
done_(it->first, it->second->result().GetError());
|
||||
}
|
||||
};
|
||||
resolver_ptr->Start(address, family, std::move(callback));
|
||||
}
|
||||
|
||||
bool UDPPort::AddressResolver::GetResolvedAddress(
|
||||
const rtc::SocketAddress& input,
|
||||
int family,
|
||||
rtc::SocketAddress* output) const {
|
||||
ResolverMap::const_iterator it = resolvers_.find(input);
|
||||
if (it == resolvers_.end())
|
||||
return false;
|
||||
|
||||
return it->second->result().GetResolvedAddress(family, output);
|
||||
}
|
||||
|
||||
UDPPort::UDPPort(rtc::Thread* thread,
|
||||
absl::string_view type,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
rtc::AsyncPacketSocket* socket,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
bool emit_local_for_anyaddress,
|
||||
const webrtc::FieldTrialsView* field_trials)
|
||||
: Port(thread, type, factory, network, username, password, field_trials),
|
||||
request_manager_(
|
||||
thread,
|
||||
[this](const void* data, size_t size, StunRequest* request) {
|
||||
OnSendPacket(data, size, request);
|
||||
}),
|
||||
socket_(socket),
|
||||
error_(0),
|
||||
ready_(false),
|
||||
stun_keepalive_delay_(STUN_KEEPALIVE_INTERVAL),
|
||||
dscp_(rtc::DSCP_NO_CHANGE),
|
||||
emit_local_for_anyaddress_(emit_local_for_anyaddress) {}
|
||||
|
||||
UDPPort::UDPPort(rtc::Thread* thread,
|
||||
absl::string_view type,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
bool emit_local_for_anyaddress,
|
||||
const webrtc::FieldTrialsView* field_trials)
|
||||
: Port(thread,
|
||||
type,
|
||||
factory,
|
||||
network,
|
||||
min_port,
|
||||
max_port,
|
||||
username,
|
||||
password,
|
||||
field_trials),
|
||||
request_manager_(
|
||||
thread,
|
||||
[this](const void* data, size_t size, StunRequest* request) {
|
||||
OnSendPacket(data, size, request);
|
||||
}),
|
||||
socket_(nullptr),
|
||||
error_(0),
|
||||
ready_(false),
|
||||
stun_keepalive_delay_(STUN_KEEPALIVE_INTERVAL),
|
||||
dscp_(rtc::DSCP_NO_CHANGE),
|
||||
emit_local_for_anyaddress_(emit_local_for_anyaddress) {}
|
||||
|
||||
bool UDPPort::Init() {
|
||||
stun_keepalive_lifetime_ = GetStunKeepaliveLifetime();
|
||||
if (!SharedSocket()) {
|
||||
RTC_DCHECK(socket_ == nullptr);
|
||||
socket_ = socket_factory()->CreateUdpSocket(
|
||||
rtc::SocketAddress(Network()->GetBestIP(), 0), min_port(), max_port());
|
||||
if (!socket_) {
|
||||
RTC_LOG(LS_WARNING) << ToString() << ": UDP socket creation failed";
|
||||
return false;
|
||||
}
|
||||
socket_->RegisterReceivedPacketCallback(
|
||||
[&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
|
||||
OnReadPacket(socket, packet);
|
||||
});
|
||||
}
|
||||
socket_->SignalSentPacket.connect(this, &UDPPort::OnSentPacket);
|
||||
socket_->SignalReadyToSend.connect(this, &UDPPort::OnReadyToSend);
|
||||
socket_->SignalAddressReady.connect(this, &UDPPort::OnLocalAddressReady);
|
||||
return true;
|
||||
}
|
||||
|
||||
UDPPort::~UDPPort() {
|
||||
if (!SharedSocket())
|
||||
delete socket_;
|
||||
}
|
||||
|
||||
void UDPPort::PrepareAddress() {
|
||||
RTC_DCHECK(request_manager_.empty());
|
||||
if (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND) {
|
||||
OnLocalAddressReady(socket_, socket_->GetLocalAddress());
|
||||
}
|
||||
}
|
||||
|
||||
void UDPPort::MaybePrepareStunCandidate() {
|
||||
// Sending binding request to the STUN server if address is available to
|
||||
// prepare STUN candidate.
|
||||
if (!server_addresses_.empty()) {
|
||||
SendStunBindingRequests();
|
||||
} else {
|
||||
// Port is done allocating candidates.
|
||||
MaybeSetPortCompleteOrError();
|
||||
}
|
||||
}
|
||||
|
||||
Connection* UDPPort::CreateConnection(const Candidate& address,
|
||||
CandidateOrigin origin) {
|
||||
if (!SupportsProtocol(address.protocol())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!IsCompatibleAddress(address.address())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// In addition to DCHECK-ing the non-emptiness of local candidates, we also
|
||||
// skip this Port with null if there are latent bugs to violate it; otherwise
|
||||
// it would lead to a crash when accessing the local candidate of the
|
||||
// connection that would be created below.
|
||||
if (Candidates().empty()) {
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return nullptr;
|
||||
}
|
||||
// When the socket is shared, the srflx candidate is gathered by the UDPPort.
|
||||
// The assumption here is that
|
||||
// 1) if the IP concealment with mDNS is not enabled, the gathering of the
|
||||
// host candidate of this port (which is synchronous),
|
||||
// 2) or otherwise if enabled, the start of name registration of the host
|
||||
// candidate (as the start of asynchronous gathering)
|
||||
// is always before the gathering of a srflx candidate (and any prflx
|
||||
// candidate).
|
||||
//
|
||||
// See also the definition of MdnsNameRegistrationStatus::kNotStarted in
|
||||
// port.h.
|
||||
RTC_DCHECK(!SharedSocket() || Candidates()[0].is_local() ||
|
||||
mdns_name_registration_status() !=
|
||||
MdnsNameRegistrationStatus::kNotStarted);
|
||||
|
||||
Connection* conn = new ProxyConnection(NewWeakPtr(), 0, address);
|
||||
AddOrReplaceConnection(conn);
|
||||
return conn;
|
||||
}
|
||||
|
||||
int UDPPort::SendTo(const void* data,
|
||||
size_t size,
|
||||
const rtc::SocketAddress& addr,
|
||||
const rtc::PacketOptions& options,
|
||||
bool payload) {
|
||||
rtc::PacketOptions modified_options(options);
|
||||
CopyPortInformationToPacketInfo(&modified_options.info_signaled_after_sent);
|
||||
int sent = socket_->SendTo(data, size, addr, modified_options);
|
||||
if (sent < 0) {
|
||||
error_ = socket_->GetError();
|
||||
// Rate limiting added for crbug.com/856088.
|
||||
// TODO(webrtc:9622): Use general rate limiting mechanism once it exists.
|
||||
if (send_error_count_ < kSendErrorLogLimit) {
|
||||
++send_error_count_;
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": UDP send of " << size
|
||||
<< " bytes to host "
|
||||
<< addr.ToSensitiveNameAndAddressString()
|
||||
<< " failed with error " << error_;
|
||||
}
|
||||
} else {
|
||||
send_error_count_ = 0;
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
void UDPPort::UpdateNetworkCost() {
|
||||
Port::UpdateNetworkCost();
|
||||
stun_keepalive_lifetime_ = GetStunKeepaliveLifetime();
|
||||
}
|
||||
|
||||
rtc::DiffServCodePoint UDPPort::StunDscpValue() const {
|
||||
return dscp_;
|
||||
}
|
||||
|
||||
int UDPPort::SetOption(rtc::Socket::Option opt, int value) {
|
||||
if (opt == rtc::Socket::OPT_DSCP) {
|
||||
// Save value for future packets we instantiate.
|
||||
dscp_ = static_cast<rtc::DiffServCodePoint>(value);
|
||||
}
|
||||
return socket_->SetOption(opt, value);
|
||||
}
|
||||
|
||||
int UDPPort::GetOption(rtc::Socket::Option opt, int* value) {
|
||||
return socket_->GetOption(opt, value);
|
||||
}
|
||||
|
||||
int UDPPort::GetError() {
|
||||
return error_;
|
||||
}
|
||||
|
||||
bool UDPPort::HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet) {
|
||||
// All packets given to UDP port will be consumed.
|
||||
OnReadPacket(socket, packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UDPPort::SupportsProtocol(absl::string_view protocol) const {
|
||||
return protocol == UDP_PROTOCOL_NAME;
|
||||
}
|
||||
|
||||
ProtocolType UDPPort::GetProtocol() const {
|
||||
return PROTO_UDP;
|
||||
}
|
||||
|
||||
void UDPPort::GetStunStats(absl::optional<StunStats>* stats) {
|
||||
*stats = stats_;
|
||||
}
|
||||
|
||||
void UDPPort::set_stun_keepalive_delay(const absl::optional<int>& delay) {
|
||||
stun_keepalive_delay_ = delay.value_or(STUN_KEEPALIVE_INTERVAL);
|
||||
}
|
||||
|
||||
void UDPPort::OnLocalAddressReady(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::SocketAddress& address) {
|
||||
// When adapter enumeration is disabled and binding to the any address, the
|
||||
// default local address will be issued as a candidate instead if
|
||||
// `emit_local_for_anyaddress` is true. This is to allow connectivity for
|
||||
// applications which absolutely requires a HOST candidate.
|
||||
rtc::SocketAddress addr = address;
|
||||
|
||||
// If MaybeSetDefaultLocalAddress fails, we keep the "any" IP so that at
|
||||
// least the port is listening.
|
||||
MaybeSetDefaultLocalAddress(&addr);
|
||||
|
||||
AddAddress(addr, addr, rtc::SocketAddress(), UDP_PROTOCOL_NAME, "", "",
|
||||
LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST, 0, "", false);
|
||||
MaybePrepareStunCandidate();
|
||||
}
|
||||
|
||||
void UDPPort::PostAddAddress(bool is_final) {
|
||||
MaybeSetPortCompleteOrError();
|
||||
}
|
||||
|
||||
void UDPPort::OnReadPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet) {
|
||||
RTC_DCHECK(socket == socket_);
|
||||
RTC_DCHECK(!packet.source_address().IsUnresolvedIP());
|
||||
|
||||
// Look for a response from the STUN server.
|
||||
// Even if the response doesn't match one of our outstanding requests, we
|
||||
// will eat it because it might be a response to a retransmitted packet, and
|
||||
// we already cleared the request when we got the first response.
|
||||
if (server_addresses_.find(packet.source_address()) !=
|
||||
server_addresses_.end()) {
|
||||
request_manager_.CheckResponse(
|
||||
reinterpret_cast<const char*>(packet.payload().data()),
|
||||
packet.payload().size());
|
||||
return;
|
||||
}
|
||||
|
||||
if (Connection* conn = GetConnection(packet.source_address())) {
|
||||
conn->OnReadPacket(packet);
|
||||
} else {
|
||||
Port::OnReadPacket(packet, PROTO_UDP);
|
||||
}
|
||||
}
|
||||
|
||||
void UDPPort::OnSentPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::SentPacket& sent_packet) {
|
||||
PortInterface::SignalSentPacket(sent_packet);
|
||||
}
|
||||
|
||||
void UDPPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
|
||||
Port::OnReadyToSend();
|
||||
}
|
||||
|
||||
void UDPPort::SendStunBindingRequests() {
|
||||
// We will keep pinging the stun server to make sure our NAT pin-hole stays
|
||||
// open until the deadline (specified in SendStunBindingRequest).
|
||||
RTC_DCHECK(request_manager_.empty());
|
||||
|
||||
for (ServerAddresses::const_iterator it = server_addresses_.begin();
|
||||
it != server_addresses_.end();) {
|
||||
// sending a STUN binding request may cause the current SocketAddress to be
|
||||
// erased from the set, invalidating the loop iterator before it is
|
||||
// incremented (even if the SocketAddress itself still exists). So make a
|
||||
// copy of the loop iterator, which may be safely invalidated.
|
||||
ServerAddresses::const_iterator addr = it++;
|
||||
SendStunBindingRequest(*addr);
|
||||
}
|
||||
}
|
||||
|
||||
void UDPPort::ResolveStunAddress(const rtc::SocketAddress& stun_addr) {
|
||||
if (!resolver_) {
|
||||
resolver_.reset(new AddressResolver(
|
||||
socket_factory(), [&](const rtc::SocketAddress& input, int error) {
|
||||
OnResolveResult(input, error);
|
||||
}));
|
||||
}
|
||||
|
||||
RTC_LOG(LS_INFO) << ToString() << ": Starting STUN host lookup for "
|
||||
<< stun_addr.ToSensitiveString();
|
||||
resolver_->Resolve(stun_addr, Network()->family(), field_trials());
|
||||
}
|
||||
|
||||
void UDPPort::OnResolveResult(const rtc::SocketAddress& input, int error) {
|
||||
RTC_DCHECK(resolver_.get() != nullptr);
|
||||
|
||||
rtc::SocketAddress resolved;
|
||||
if (error != 0 || !resolver_->GetResolvedAddress(
|
||||
input, Network()->GetBestIP().family(), &resolved)) {
|
||||
RTC_LOG(LS_WARNING) << ToString()
|
||||
<< ": StunPort: stun host lookup received error "
|
||||
<< error;
|
||||
OnStunBindingOrResolveRequestFailed(input, SERVER_NOT_REACHABLE_ERROR,
|
||||
"STUN host lookup received error.");
|
||||
return;
|
||||
}
|
||||
|
||||
server_addresses_.erase(input);
|
||||
|
||||
if (server_addresses_.find(resolved) == server_addresses_.end()) {
|
||||
server_addresses_.insert(resolved);
|
||||
SendStunBindingRequest(resolved);
|
||||
}
|
||||
}
|
||||
|
||||
void UDPPort::SendStunBindingRequest(const rtc::SocketAddress& stun_addr) {
|
||||
if (stun_addr.IsUnresolvedIP()) {
|
||||
ResolveStunAddress(stun_addr);
|
||||
|
||||
} else if (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND) {
|
||||
// Check if `server_addr_` is compatible with the port's ip.
|
||||
if (IsCompatibleAddress(stun_addr)) {
|
||||
request_manager_.Send(
|
||||
new StunBindingRequest(this, stun_addr, rtc::TimeMillis()));
|
||||
} else {
|
||||
// Since we can't send stun messages to the server, we should mark this
|
||||
// port ready.
|
||||
const char* reason = "STUN server address is incompatible.";
|
||||
RTC_LOG(LS_WARNING) << reason;
|
||||
OnStunBindingOrResolveRequestFailed(stun_addr, SERVER_NOT_REACHABLE_ERROR,
|
||||
reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UDPPort::MaybeSetDefaultLocalAddress(rtc::SocketAddress* addr) const {
|
||||
if (!addr->IsAnyIP() || !emit_local_for_anyaddress_ ||
|
||||
!Network()->default_local_address_provider()) {
|
||||
return true;
|
||||
}
|
||||
rtc::IPAddress default_address;
|
||||
bool result =
|
||||
Network()->default_local_address_provider()->GetDefaultLocalAddress(
|
||||
addr->family(), &default_address);
|
||||
if (!result || default_address.IsNil()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
addr->SetIP(default_address);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UDPPort::OnStunBindingRequestSucceeded(
|
||||
int rtt_ms,
|
||||
const rtc::SocketAddress& stun_server_addr,
|
||||
const rtc::SocketAddress& stun_reflected_addr) {
|
||||
RTC_DCHECK(stats_.stun_binding_responses_received <
|
||||
stats_.stun_binding_requests_sent);
|
||||
stats_.stun_binding_responses_received++;
|
||||
stats_.stun_binding_rtt_ms_total += rtt_ms;
|
||||
stats_.stun_binding_rtt_ms_squared_total += rtt_ms * rtt_ms;
|
||||
if (bind_request_succeeded_servers_.find(stun_server_addr) !=
|
||||
bind_request_succeeded_servers_.end()) {
|
||||
return;
|
||||
}
|
||||
bind_request_succeeded_servers_.insert(stun_server_addr);
|
||||
// If socket is shared and `stun_reflected_addr` is equal to local socket
|
||||
// address and mDNS obfuscation is not enabled, or if the same address has
|
||||
// been added by another STUN server, then discarding the stun address.
|
||||
// For STUN, related address is the local socket address.
|
||||
if ((!SharedSocket() || stun_reflected_addr != socket_->GetLocalAddress() ||
|
||||
Network()->GetMdnsResponder() != nullptr) &&
|
||||
!HasStunCandidateWithAddress(stun_reflected_addr)) {
|
||||
rtc::SocketAddress related_address = socket_->GetLocalAddress();
|
||||
// If we can't stamp the related address correctly, empty it to avoid leak.
|
||||
if (!MaybeSetDefaultLocalAddress(&related_address)) {
|
||||
related_address =
|
||||
rtc::EmptySocketAddressWithFamily(related_address.family());
|
||||
}
|
||||
|
||||
rtc::StringBuilder url;
|
||||
url << "stun:" << stun_server_addr.hostname() << ":"
|
||||
<< stun_server_addr.port();
|
||||
AddAddress(stun_reflected_addr, socket_->GetLocalAddress(), related_address,
|
||||
UDP_PROTOCOL_NAME, "", "", STUN_PORT_TYPE,
|
||||
ICE_TYPE_PREFERENCE_SRFLX, 0, url.str(), false);
|
||||
}
|
||||
MaybeSetPortCompleteOrError();
|
||||
}
|
||||
|
||||
void UDPPort::OnStunBindingOrResolveRequestFailed(
|
||||
const rtc::SocketAddress& stun_server_addr,
|
||||
int error_code,
|
||||
absl::string_view reason) {
|
||||
rtc::StringBuilder url;
|
||||
url << "stun:" << stun_server_addr.ToString();
|
||||
SignalCandidateError(
|
||||
this, IceCandidateErrorEvent(GetLocalAddress().HostAsSensitiveURIString(),
|
||||
GetLocalAddress().port(), url.str(),
|
||||
error_code, reason));
|
||||
if (bind_request_failed_servers_.find(stun_server_addr) !=
|
||||
bind_request_failed_servers_.end()) {
|
||||
return;
|
||||
}
|
||||
bind_request_failed_servers_.insert(stun_server_addr);
|
||||
MaybeSetPortCompleteOrError();
|
||||
}
|
||||
|
||||
void UDPPort::MaybeSetPortCompleteOrError() {
|
||||
if (mdns_name_registration_status() ==
|
||||
MdnsNameRegistrationStatus::kInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ready_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not set port ready if we are still waiting for bind responses.
|
||||
const size_t servers_done_bind_request =
|
||||
bind_request_failed_servers_.size() +
|
||||
bind_request_succeeded_servers_.size();
|
||||
if (server_addresses_.size() != servers_done_bind_request) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Setting ready status.
|
||||
ready_ = true;
|
||||
|
||||
// The port is "completed" if there is no stun server provided, or the bind
|
||||
// request succeeded for any stun server, or the socket is shared.
|
||||
if (server_addresses_.empty() || bind_request_succeeded_servers_.size() > 0 ||
|
||||
SharedSocket()) {
|
||||
SignalPortComplete(this);
|
||||
} else {
|
||||
SignalPortError(this);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(?): merge this with SendTo above.
|
||||
void UDPPort::OnSendPacket(const void* data, size_t size, StunRequest* req) {
|
||||
StunBindingRequest* sreq = static_cast<StunBindingRequest*>(req);
|
||||
rtc::PacketOptions options(StunDscpValue());
|
||||
options.info_signaled_after_sent.packet_type = rtc::PacketType::kStunMessage;
|
||||
CopyPortInformationToPacketInfo(&options.info_signaled_after_sent);
|
||||
if (socket_->SendTo(data, size, sreq->server_addr(), options) < 0) {
|
||||
RTC_LOG_ERR_EX(LS_ERROR, socket_->GetError())
|
||||
<< "UDP send of " << size << " bytes to host "
|
||||
<< sreq->server_addr().ToSensitiveNameAndAddressString()
|
||||
<< " failed with error " << error_;
|
||||
}
|
||||
stats_.stun_binding_requests_sent++;
|
||||
}
|
||||
|
||||
bool UDPPort::HasStunCandidateWithAddress(
|
||||
const rtc::SocketAddress& addr) const {
|
||||
const std::vector<Candidate>& existing_candidates = Candidates();
|
||||
std::vector<Candidate>::const_iterator it = existing_candidates.begin();
|
||||
for (; it != existing_candidates.end(); ++it) {
|
||||
if (it->is_stun() && it->address() == addr)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<StunPort> StunPort::Create(
|
||||
rtc::Thread* thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
const ServerAddresses& servers,
|
||||
absl::optional<int> stun_keepalive_interval,
|
||||
const webrtc::FieldTrialsView* field_trials) {
|
||||
// Using `new` to access a non-public constructor.
|
||||
auto port = absl::WrapUnique(new StunPort(thread, factory, network, min_port,
|
||||
max_port, username, password,
|
||||
servers, field_trials));
|
||||
port->set_stun_keepalive_delay(stun_keepalive_interval);
|
||||
if (!port->Init()) {
|
||||
return nullptr;
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
StunPort::StunPort(rtc::Thread* thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
const ServerAddresses& servers,
|
||||
const webrtc::FieldTrialsView* field_trials)
|
||||
: UDPPort(thread,
|
||||
STUN_PORT_TYPE,
|
||||
factory,
|
||||
network,
|
||||
min_port,
|
||||
max_port,
|
||||
username,
|
||||
password,
|
||||
false,
|
||||
field_trials) {
|
||||
set_server_addresses(servers);
|
||||
}
|
||||
|
||||
void StunPort::PrepareAddress() {
|
||||
SendStunBindingRequests();
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
298
TMessagesProj/jni/voip/webrtc/p2p/base/stun_port.h
Normal file
298
TMessagesProj/jni/voip/webrtc/p2p/base/stun_port.h
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_STUN_PORT_H_
|
||||
#define P2P_BASE_STUN_PORT_H_
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "p2p/base/stun_request.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/network/received_packet.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// Lifetime chosen for STUN ports on low-cost networks.
|
||||
static const int INFINITE_LIFETIME = -1;
|
||||
// Lifetime for STUN ports on high-cost networks: 2 minutes
|
||||
static const int HIGH_COST_PORT_KEEPALIVE_LIFETIME = 2 * 60 * 1000;
|
||||
|
||||
// Communicates using the address on the outside of a NAT.
|
||||
class RTC_EXPORT UDPPort : public Port {
|
||||
public:
|
||||
static std::unique_ptr<UDPPort> Create(
|
||||
rtc::Thread* thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
rtc::AsyncPacketSocket* socket,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
bool emit_local_for_anyaddress,
|
||||
absl::optional<int> stun_keepalive_interval,
|
||||
const webrtc::FieldTrialsView* field_trials = nullptr) {
|
||||
// Using `new` to access a non-public constructor.
|
||||
auto port = absl::WrapUnique(
|
||||
new UDPPort(thread, LOCAL_PORT_TYPE, factory, network, socket, username,
|
||||
password, emit_local_for_anyaddress, field_trials));
|
||||
port->set_stun_keepalive_delay(stun_keepalive_interval);
|
||||
if (!port->Init()) {
|
||||
return nullptr;
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
static std::unique_ptr<UDPPort> Create(
|
||||
rtc::Thread* thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
bool emit_local_for_anyaddress,
|
||||
absl::optional<int> stun_keepalive_interval,
|
||||
const webrtc::FieldTrialsView* field_trials = nullptr) {
|
||||
// Using `new` to access a non-public constructor.
|
||||
auto port = absl::WrapUnique(new UDPPort(
|
||||
thread, LOCAL_PORT_TYPE, factory, network, min_port, max_port, username,
|
||||
password, emit_local_for_anyaddress, field_trials));
|
||||
port->set_stun_keepalive_delay(stun_keepalive_interval);
|
||||
if (!port->Init()) {
|
||||
return nullptr;
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
~UDPPort() override;
|
||||
|
||||
rtc::SocketAddress GetLocalAddress() const {
|
||||
return socket_->GetLocalAddress();
|
||||
}
|
||||
|
||||
const ServerAddresses& server_addresses() const { return server_addresses_; }
|
||||
void set_server_addresses(const ServerAddresses& addresses) {
|
||||
server_addresses_ = addresses;
|
||||
}
|
||||
|
||||
void PrepareAddress() override;
|
||||
|
||||
Connection* CreateConnection(const Candidate& address,
|
||||
CandidateOrigin origin) override;
|
||||
int SetOption(rtc::Socket::Option opt, int value) override;
|
||||
int GetOption(rtc::Socket::Option opt, int* value) override;
|
||||
int GetError() override;
|
||||
|
||||
bool HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet) override;
|
||||
|
||||
bool SupportsProtocol(absl::string_view protocol) const override;
|
||||
ProtocolType GetProtocol() const override;
|
||||
|
||||
void GetStunStats(absl::optional<StunStats>* stats) override;
|
||||
|
||||
void set_stun_keepalive_delay(const absl::optional<int>& delay);
|
||||
int stun_keepalive_delay() const { return stun_keepalive_delay_; }
|
||||
|
||||
// Visible for testing.
|
||||
int stun_keepalive_lifetime() const { return stun_keepalive_lifetime_; }
|
||||
void set_stun_keepalive_lifetime(int lifetime) {
|
||||
stun_keepalive_lifetime_ = lifetime;
|
||||
}
|
||||
|
||||
StunRequestManager& request_manager() { return request_manager_; }
|
||||
|
||||
protected:
|
||||
UDPPort(rtc::Thread* thread,
|
||||
absl::string_view type,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
bool emit_local_for_anyaddress,
|
||||
const webrtc::FieldTrialsView* field_trials);
|
||||
|
||||
UDPPort(rtc::Thread* thread,
|
||||
absl::string_view type,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
rtc::AsyncPacketSocket* socket,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
bool emit_local_for_anyaddress,
|
||||
const webrtc::FieldTrialsView* field_trials);
|
||||
|
||||
bool Init();
|
||||
|
||||
int SendTo(const void* data,
|
||||
size_t size,
|
||||
const rtc::SocketAddress& addr,
|
||||
const rtc::PacketOptions& options,
|
||||
bool payload) override;
|
||||
|
||||
void UpdateNetworkCost() override;
|
||||
|
||||
rtc::DiffServCodePoint StunDscpValue() const override;
|
||||
|
||||
void OnLocalAddressReady(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::SocketAddress& address);
|
||||
|
||||
void PostAddAddress(bool is_final) override;
|
||||
|
||||
void OnReadPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet);
|
||||
|
||||
void OnSentPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::SentPacket& sent_packet) override;
|
||||
|
||||
void OnReadyToSend(rtc::AsyncPacketSocket* socket);
|
||||
|
||||
// This method will send STUN binding request if STUN server address is set.
|
||||
void MaybePrepareStunCandidate();
|
||||
|
||||
void SendStunBindingRequests();
|
||||
|
||||
// Helper function which will set `addr`'s IP to the default local address if
|
||||
// `addr` is the "any" address and `emit_local_for_anyaddress_` is true. When
|
||||
// returning false, it indicates that the operation has failed and the
|
||||
// address shouldn't be used by any candidate.
|
||||
bool MaybeSetDefaultLocalAddress(rtc::SocketAddress* addr) const;
|
||||
|
||||
private:
|
||||
// A helper class which can be called repeatedly to resolve multiple
|
||||
// addresses, as opposed to rtc::AsyncDnsResolverInterface, which can only
|
||||
// resolve one address per instance.
|
||||
class AddressResolver {
|
||||
public:
|
||||
explicit AddressResolver(
|
||||
rtc::PacketSocketFactory* factory,
|
||||
std::function<void(const rtc::SocketAddress&, int)> done_callback);
|
||||
|
||||
void Resolve(const rtc::SocketAddress& address,
|
||||
int family,
|
||||
const webrtc::FieldTrialsView& field_trials);
|
||||
bool GetResolvedAddress(const rtc::SocketAddress& input,
|
||||
int family,
|
||||
rtc::SocketAddress* output) const;
|
||||
|
||||
private:
|
||||
typedef std::map<rtc::SocketAddress,
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface>>
|
||||
ResolverMap;
|
||||
|
||||
rtc::PacketSocketFactory* socket_factory_;
|
||||
// The function is called when resolving the specified address is finished.
|
||||
// The first argument is the input address, the second argument is the error
|
||||
// or 0 if it succeeded.
|
||||
std::function<void(const rtc::SocketAddress&, int)> done_;
|
||||
// Resolver may fire callbacks that refer to done_, so ensure
|
||||
// that all resolvers are destroyed first.
|
||||
ResolverMap resolvers_;
|
||||
};
|
||||
|
||||
// DNS resolution of the STUN server.
|
||||
void ResolveStunAddress(const rtc::SocketAddress& stun_addr);
|
||||
void OnResolveResult(const rtc::SocketAddress& input, int error);
|
||||
|
||||
// Send a STUN binding request to the given address. Calling this method may
|
||||
// cause the set of known server addresses to be modified, eg. by replacing an
|
||||
// unresolved server address with a resolved address.
|
||||
void SendStunBindingRequest(const rtc::SocketAddress& stun_addr);
|
||||
|
||||
// Below methods handles binding request responses.
|
||||
void OnStunBindingRequestSucceeded(
|
||||
int rtt_ms,
|
||||
const rtc::SocketAddress& stun_server_addr,
|
||||
const rtc::SocketAddress& stun_reflected_addr);
|
||||
void OnStunBindingOrResolveRequestFailed(
|
||||
const rtc::SocketAddress& stun_server_addr,
|
||||
int error_code,
|
||||
absl::string_view reason);
|
||||
|
||||
// Sends STUN requests to the server.
|
||||
void OnSendPacket(const void* data, size_t size, StunRequest* req);
|
||||
|
||||
// TODO(mallinaht) - Move this up to cricket::Port when SignalAddressReady is
|
||||
// changed to SignalPortReady.
|
||||
void MaybeSetPortCompleteOrError();
|
||||
|
||||
bool HasStunCandidateWithAddress(const rtc::SocketAddress& addr) const;
|
||||
|
||||
// If this is a low-cost network, it will keep on sending STUN binding
|
||||
// requests indefinitely to keep the NAT binding alive. Otherwise, stop
|
||||
// sending STUN binding requests after HIGH_COST_PORT_KEEPALIVE_LIFETIME.
|
||||
int GetStunKeepaliveLifetime() {
|
||||
return (network_cost() >= rtc::kNetworkCostHigh)
|
||||
? HIGH_COST_PORT_KEEPALIVE_LIFETIME
|
||||
: INFINITE_LIFETIME;
|
||||
}
|
||||
|
||||
ServerAddresses server_addresses_;
|
||||
ServerAddresses bind_request_succeeded_servers_;
|
||||
ServerAddresses bind_request_failed_servers_;
|
||||
StunRequestManager request_manager_;
|
||||
rtc::AsyncPacketSocket* socket_;
|
||||
int error_;
|
||||
int send_error_count_ = 0;
|
||||
std::unique_ptr<AddressResolver> resolver_;
|
||||
bool ready_;
|
||||
int stun_keepalive_delay_;
|
||||
int stun_keepalive_lifetime_ = INFINITE_LIFETIME;
|
||||
rtc::DiffServCodePoint dscp_;
|
||||
|
||||
StunStats stats_;
|
||||
|
||||
// This is true by default and false when
|
||||
// PORTALLOCATOR_DISABLE_DEFAULT_LOCAL_CANDIDATE is specified.
|
||||
bool emit_local_for_anyaddress_;
|
||||
|
||||
friend class StunBindingRequest;
|
||||
};
|
||||
|
||||
class StunPort : public UDPPort {
|
||||
public:
|
||||
static std::unique_ptr<StunPort> Create(
|
||||
rtc::Thread* thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
const ServerAddresses& servers,
|
||||
absl::optional<int> stun_keepalive_interval,
|
||||
const webrtc::FieldTrialsView* field_trials);
|
||||
|
||||
void PrepareAddress() override;
|
||||
|
||||
protected:
|
||||
StunPort(rtc::Thread* thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
const ServerAddresses& servers,
|
||||
const webrtc::FieldTrialsView* field_trials);
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_STUN_PORT_H_
|
||||
331
TMessagesProj/jni/voip/webrtc/p2p/base/stun_request.cc
Normal file
331
TMessagesProj/jni/voip/webrtc/p2p/base/stun_request.cc
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/stun_request.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/helpers.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/string_encode.h"
|
||||
#include "rtc_base/time_utils.h" // For TimeMillis
|
||||
|
||||
namespace cricket {
|
||||
using ::webrtc::SafeTask;
|
||||
|
||||
// RFC 5389 says SHOULD be 500ms.
|
||||
// For years, this was 100ms, but for networks that
|
||||
// experience moments of high RTT (such as 2G networks), this doesn't
|
||||
// work well.
|
||||
const int STUN_INITIAL_RTO = 250; // milliseconds
|
||||
|
||||
// The timeout doubles each retransmission, up to this many times
|
||||
// RFC 5389 says SHOULD retransmit 7 times.
|
||||
// This has been 8 for years (not sure why).
|
||||
const int STUN_MAX_RETRANSMISSIONS = 8; // Total sends: 9
|
||||
|
||||
// We also cap the doubling, even though the standard doesn't say to.
|
||||
// This has been 1.6 seconds for years, but for networks that
|
||||
// experience moments of high RTT (such as 2G networks), this doesn't
|
||||
// work well.
|
||||
const int STUN_MAX_RTO = 8000; // milliseconds, or 5 doublings
|
||||
|
||||
StunRequestManager::StunRequestManager(
|
||||
webrtc::TaskQueueBase* thread,
|
||||
std::function<void(const void*, size_t, StunRequest*)> send_packet)
|
||||
: thread_(thread), send_packet_(std::move(send_packet)) {}
|
||||
|
||||
StunRequestManager::~StunRequestManager() = default;
|
||||
|
||||
void StunRequestManager::Send(StunRequest* request) {
|
||||
SendDelayed(request, 0);
|
||||
}
|
||||
|
||||
void StunRequestManager::SendDelayed(StunRequest* request, int delay) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
RTC_DCHECK_EQ(this, request->manager());
|
||||
RTC_DCHECK(!request->AuthenticationRequired() ||
|
||||
request->msg()->integrity() !=
|
||||
StunMessage::IntegrityStatus::kNotSet)
|
||||
<< "Sending request w/o integrity!";
|
||||
auto [iter, was_inserted] =
|
||||
requests_.emplace(request->id(), absl::WrapUnique(request));
|
||||
RTC_DCHECK(was_inserted);
|
||||
request->Send(webrtc::TimeDelta::Millis(delay));
|
||||
}
|
||||
|
||||
void StunRequestManager::FlushForTest(int msg_type) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
for (const auto& [unused, request] : requests_) {
|
||||
if (msg_type == kAllRequestsForTest || msg_type == request->type()) {
|
||||
// Calling `Send` implies starting the send operation which may be posted
|
||||
// on a timer and be repeated on a timer until timeout. To make sure that
|
||||
// a call to `Send` doesn't conflict with a previously started `Send`
|
||||
// operation, we reset the `task_safety_` flag here, which has the effect
|
||||
// of canceling any outstanding tasks and prepare a new flag for
|
||||
// operations related to this call to `Send`.
|
||||
request->ResetTasksForTest();
|
||||
request->Send(webrtc::TimeDelta::Zero());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool StunRequestManager::HasRequestForTest(int msg_type) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
RTC_DCHECK_NE(msg_type, kAllRequestsForTest);
|
||||
for (const auto& [unused, request] : requests_) {
|
||||
if (msg_type == request->type()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void StunRequestManager::Clear() {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
requests_.clear();
|
||||
}
|
||||
|
||||
bool StunRequestManager::CheckResponse(StunMessage* msg) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
RequestMap::iterator iter = requests_.find(msg->transaction_id());
|
||||
if (iter == requests_.end())
|
||||
return false;
|
||||
|
||||
StunRequest* request = iter->second.get();
|
||||
|
||||
// Now that we know the request, we can see if the response is
|
||||
// integrity-protected or not. Some requests explicitly disables
|
||||
// integrity checks using SetAuthenticationRequired.
|
||||
// TODO(chromium:1177125): Remove below!
|
||||
// And we suspect that for some tests, the message integrity is not set in the
|
||||
// request. Complain, and then don't check.
|
||||
bool skip_integrity_checking =
|
||||
(request->msg()->integrity() == StunMessage::IntegrityStatus::kNotSet);
|
||||
if (!request->AuthenticationRequired()) {
|
||||
// This is a STUN_BINDING to from stun_port.cc or
|
||||
// the initial (unauthenticated) TURN_ALLOCATE_REQUEST.
|
||||
} else if (skip_integrity_checking) {
|
||||
// TODO(chromium:1177125): Remove below!
|
||||
// This indicates lazy test writing (not adding integrity attribute).
|
||||
// Complain, but only in debug mode (while developing).
|
||||
RTC_LOG(LS_ERROR)
|
||||
<< "CheckResponse called on a passwordless request. Fix test!";
|
||||
RTC_DCHECK(false)
|
||||
<< "CheckResponse called on a passwordless request. Fix test!";
|
||||
} else {
|
||||
if (msg->integrity() == StunMessage::IntegrityStatus::kNotSet) {
|
||||
// Checking status for the first time. Normal.
|
||||
msg->ValidateMessageIntegrity(request->msg()->password());
|
||||
} else if (msg->integrity() == StunMessage::IntegrityStatus::kIntegrityOk &&
|
||||
msg->password() == request->msg()->password()) {
|
||||
// Status is already checked, with the same password. This is the case
|
||||
// we would want to see happen.
|
||||
} else if (msg->integrity() ==
|
||||
StunMessage::IntegrityStatus::kIntegrityBad) {
|
||||
// This indicates that the original check had the wrong password.
|
||||
// Bad design, needs revisiting.
|
||||
// TODO(crbug.com/1177125): Fix this.
|
||||
msg->RevalidateMessageIntegrity(request->msg()->password());
|
||||
} else {
|
||||
RTC_CHECK_NOTREACHED();
|
||||
}
|
||||
}
|
||||
|
||||
if (!msg->GetNonComprehendedAttributes().empty()) {
|
||||
// If a response contains unknown comprehension-required attributes, it's
|
||||
// simply discarded and the transaction is considered failed. See RFC5389
|
||||
// sections 7.3.3 and 7.3.4.
|
||||
RTC_LOG(LS_ERROR) << ": Discarding response due to unknown "
|
||||
"comprehension-required attribute.";
|
||||
requests_.erase(iter);
|
||||
return false;
|
||||
} else if (msg->type() == GetStunSuccessResponseType(request->type())) {
|
||||
if (!msg->IntegrityOk() && !skip_integrity_checking) {
|
||||
return false;
|
||||
}
|
||||
// Erase element from hash before calling callback. This ensures
|
||||
// that the callback can modify the StunRequestManager any way it
|
||||
// sees fit.
|
||||
std::unique_ptr<StunRequest> owned_request = std::move(iter->second);
|
||||
requests_.erase(iter);
|
||||
owned_request->OnResponse(msg);
|
||||
return true;
|
||||
} else if (msg->type() == GetStunErrorResponseType(request->type())) {
|
||||
// Erase element from hash before calling callback. This ensures
|
||||
// that the callback can modify the StunRequestManager any way it
|
||||
// sees fit.
|
||||
std::unique_ptr<StunRequest> owned_request = std::move(iter->second);
|
||||
requests_.erase(iter);
|
||||
owned_request->OnErrorResponse(msg);
|
||||
return true;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Received response with wrong type: " << msg->type()
|
||||
<< " (expecting "
|
||||
<< GetStunSuccessResponseType(request->type()) << ")";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool StunRequestManager::empty() const {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
return requests_.empty();
|
||||
}
|
||||
|
||||
bool StunRequestManager::CheckResponse(const char* data, size_t size) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
// Check the appropriate bytes of the stream to see if they match the
|
||||
// transaction ID of a response we are expecting.
|
||||
|
||||
if (size < 20)
|
||||
return false;
|
||||
|
||||
std::string id;
|
||||
id.append(data + kStunTransactionIdOffset, kStunTransactionIdLength);
|
||||
|
||||
RequestMap::iterator iter = requests_.find(id);
|
||||
if (iter == requests_.end())
|
||||
return false;
|
||||
|
||||
// Parse the STUN message and continue processing as usual.
|
||||
|
||||
rtc::ByteBufferReader buf(
|
||||
rtc::MakeArrayView(reinterpret_cast<const uint8_t*>(data), size));
|
||||
std::unique_ptr<StunMessage> response(iter->second->msg_->CreateNew());
|
||||
if (!response->Read(&buf)) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to read STUN response "
|
||||
<< rtc::hex_encode(id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return CheckResponse(response.get());
|
||||
}
|
||||
|
||||
void StunRequestManager::OnRequestTimedOut(StunRequest* request) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
requests_.erase(request->id());
|
||||
}
|
||||
|
||||
void StunRequestManager::SendPacket(const void* data,
|
||||
size_t size,
|
||||
StunRequest* request) {
|
||||
RTC_DCHECK_EQ(this, request->manager());
|
||||
send_packet_(data, size, request);
|
||||
}
|
||||
|
||||
StunRequest::StunRequest(StunRequestManager& manager)
|
||||
: manager_(manager),
|
||||
msg_(new StunMessage(STUN_INVALID_MESSAGE_TYPE)),
|
||||
tstamp_(0),
|
||||
count_(0),
|
||||
timeout_(false) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
}
|
||||
|
||||
StunRequest::StunRequest(StunRequestManager& manager,
|
||||
std::unique_ptr<StunMessage> message)
|
||||
: manager_(manager),
|
||||
msg_(std::move(message)),
|
||||
tstamp_(0),
|
||||
count_(0),
|
||||
timeout_(false) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
RTC_DCHECK(!msg_->transaction_id().empty());
|
||||
}
|
||||
|
||||
StunRequest::~StunRequest() {}
|
||||
|
||||
int StunRequest::type() {
|
||||
RTC_DCHECK(msg_ != NULL);
|
||||
return msg_->type();
|
||||
}
|
||||
|
||||
const StunMessage* StunRequest::msg() const {
|
||||
return msg_.get();
|
||||
}
|
||||
|
||||
int StunRequest::Elapsed() const {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
return static_cast<int>(rtc::TimeMillis() - tstamp_);
|
||||
}
|
||||
|
||||
void StunRequest::SendInternal() {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
if (timeout_) {
|
||||
OnTimeout();
|
||||
manager_.OnRequestTimedOut(this);
|
||||
return;
|
||||
}
|
||||
|
||||
tstamp_ = rtc::TimeMillis();
|
||||
|
||||
rtc::ByteBufferWriter buf;
|
||||
msg_->Write(&buf);
|
||||
manager_.SendPacket(buf.Data(), buf.Length(), this);
|
||||
|
||||
OnSent();
|
||||
SendDelayed(webrtc::TimeDelta::Millis(resend_delay()));
|
||||
}
|
||||
|
||||
void StunRequest::SendDelayed(webrtc::TimeDelta delay) {
|
||||
network_thread()->PostDelayedTask(
|
||||
SafeTask(task_safety_.flag(), [this]() { SendInternal(); }), delay);
|
||||
}
|
||||
|
||||
void StunRequest::Send(webrtc::TimeDelta delay) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
RTC_DCHECK_GE(delay.ms(), 0);
|
||||
|
||||
RTC_DCHECK(!task_safety_.flag()->alive()) << "Send already called?";
|
||||
task_safety_.flag()->SetAlive();
|
||||
|
||||
delay.IsZero() ? SendInternal() : SendDelayed(delay);
|
||||
}
|
||||
|
||||
void StunRequest::ResetTasksForTest() {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
task_safety_.reset(webrtc::PendingTaskSafetyFlag::CreateDetachedInactive());
|
||||
count_ = 0;
|
||||
RTC_DCHECK(!timeout_);
|
||||
}
|
||||
|
||||
void StunRequest::OnSent() {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
count_ += 1;
|
||||
int retransmissions = (count_ - 1);
|
||||
if (retransmissions >= STUN_MAX_RETRANSMISSIONS) {
|
||||
timeout_ = true;
|
||||
}
|
||||
RTC_DLOG(LS_VERBOSE) << "Sent STUN request " << count_
|
||||
<< "; resend delay = " << resend_delay();
|
||||
}
|
||||
|
||||
int StunRequest::resend_delay() {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
if (count_ == 0) {
|
||||
return 0;
|
||||
}
|
||||
int retransmissions = (count_ - 1);
|
||||
int rto = STUN_INITIAL_RTO << retransmissions;
|
||||
return std::min(rto, STUN_MAX_RTO);
|
||||
}
|
||||
|
||||
void StunRequest::set_timed_out() {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
timeout_ = true;
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
169
TMessagesProj/jni/voip/webrtc/p2p/base/stun_request.h
Normal file
169
TMessagesProj/jni/voip/webrtc/p2p/base/stun_request.h
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_STUN_REQUEST_H_
|
||||
#define P2P_BASE_STUN_REQUEST_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "api/transport/stun.h"
|
||||
#include "api/units/time_delta.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class StunRequest;
|
||||
|
||||
const int kAllRequestsForTest = 0;
|
||||
|
||||
// Total max timeouts: 39.75 seconds
|
||||
// For years, this was 9.5 seconds, but for networks that experience moments of
|
||||
// high RTT (such as 40s on 2G networks), this doesn't work well.
|
||||
const int STUN_TOTAL_TIMEOUT = 39750; // milliseconds
|
||||
|
||||
// Manages a set of STUN requests, sending and resending until we receive a
|
||||
// response or determine that the request has timed out.
|
||||
class StunRequestManager {
|
||||
public:
|
||||
StunRequestManager(
|
||||
webrtc::TaskQueueBase* thread,
|
||||
std::function<void(const void*, size_t, StunRequest*)> send_packet);
|
||||
~StunRequestManager();
|
||||
|
||||
// Starts sending the given request (perhaps after a delay).
|
||||
void Send(StunRequest* request);
|
||||
void SendDelayed(StunRequest* request, int delay);
|
||||
|
||||
// If `msg_type` is kAllRequestsForTest, sends all pending requests right
|
||||
// away. Otherwise, sends those that have a matching type right away. Only for
|
||||
// testing.
|
||||
// TODO(tommi): Remove this method and update tests that use it to simulate
|
||||
// production code.
|
||||
void FlushForTest(int msg_type);
|
||||
|
||||
// Returns true if at least one request with `msg_type` is scheduled for
|
||||
// transmission. For testing only.
|
||||
// TODO(tommi): Remove this method and update tests that use it to simulate
|
||||
// production code.
|
||||
bool HasRequestForTest(int msg_type);
|
||||
|
||||
// Removes all stun requests that were added previously.
|
||||
void Clear();
|
||||
|
||||
// Determines whether the given message is a response to one of the
|
||||
// outstanding requests, and if so, processes it appropriately.
|
||||
bool CheckResponse(StunMessage* msg);
|
||||
bool CheckResponse(const char* data, size_t size);
|
||||
|
||||
// Called from a StunRequest when a timeout occurs.
|
||||
void OnRequestTimedOut(StunRequest* request);
|
||||
|
||||
bool empty() const;
|
||||
|
||||
webrtc::TaskQueueBase* network_thread() const { return thread_; }
|
||||
|
||||
void SendPacket(const void* data, size_t size, StunRequest* request);
|
||||
|
||||
private:
|
||||
typedef std::map<std::string, std::unique_ptr<StunRequest>> RequestMap;
|
||||
|
||||
webrtc::TaskQueueBase* const thread_;
|
||||
RequestMap requests_ RTC_GUARDED_BY(thread_);
|
||||
const std::function<void(const void*, size_t, StunRequest*)> send_packet_;
|
||||
};
|
||||
|
||||
// Represents an individual request to be sent. The STUN message can either be
|
||||
// constructed beforehand or built on demand.
|
||||
class StunRequest {
|
||||
public:
|
||||
explicit StunRequest(StunRequestManager& manager);
|
||||
StunRequest(StunRequestManager& manager,
|
||||
std::unique_ptr<StunMessage> message);
|
||||
virtual ~StunRequest();
|
||||
|
||||
// The manager handling this request (if it has been scheduled for sending).
|
||||
StunRequestManager* manager() { return &manager_; }
|
||||
|
||||
// Returns the transaction ID of this request.
|
||||
const std::string& id() const { return msg_->transaction_id(); }
|
||||
|
||||
// Returns the reduced transaction ID of this request.
|
||||
uint32_t reduced_transaction_id() const {
|
||||
return msg_->reduced_transaction_id();
|
||||
}
|
||||
|
||||
// Returns the STUN type of the request message.
|
||||
int type();
|
||||
|
||||
// Returns a const pointer to `msg_`.
|
||||
const StunMessage* msg() const;
|
||||
|
||||
// Time elapsed since last send (in ms)
|
||||
int Elapsed() const;
|
||||
|
||||
// Add method to explitly allow requests w/o password.
|
||||
// - STUN_BINDINGs from StunPort to a stun server
|
||||
// - The initial TURN_ALLOCATE_REQUEST
|
||||
void SetAuthenticationRequired(bool val) { authentication_required_ = val; }
|
||||
bool AuthenticationRequired() const { return authentication_required_; }
|
||||
|
||||
protected:
|
||||
friend class StunRequestManager;
|
||||
|
||||
// Called by StunRequestManager.
|
||||
void Send(webrtc::TimeDelta delay);
|
||||
|
||||
// Called from FlushForTest.
|
||||
// TODO(tommi): Remove when FlushForTest gets removed.
|
||||
void ResetTasksForTest();
|
||||
|
||||
StunMessage* mutable_msg() { return msg_.get(); }
|
||||
|
||||
// Called when the message receives a response or times out.
|
||||
virtual void OnResponse(StunMessage* response) {}
|
||||
virtual void OnErrorResponse(StunMessage* response) {}
|
||||
virtual void OnTimeout() {}
|
||||
// Called when the message is sent.
|
||||
virtual void OnSent();
|
||||
// Returns the next delay for resends in milliseconds.
|
||||
virtual int resend_delay();
|
||||
|
||||
webrtc::TaskQueueBase* network_thread() const {
|
||||
return manager_.network_thread();
|
||||
}
|
||||
|
||||
void set_timed_out();
|
||||
|
||||
private:
|
||||
void SendInternal();
|
||||
// Calls `PostDelayedTask` to queue up a call to SendInternal after the
|
||||
// specified timeout.
|
||||
void SendDelayed(webrtc::TimeDelta delay);
|
||||
|
||||
StunRequestManager& manager_;
|
||||
const std::unique_ptr<StunMessage> msg_;
|
||||
int64_t tstamp_ RTC_GUARDED_BY(network_thread());
|
||||
int count_ RTC_GUARDED_BY(network_thread());
|
||||
bool timeout_ RTC_GUARDED_BY(network_thread());
|
||||
webrtc::ScopedTaskSafety task_safety_{
|
||||
webrtc::PendingTaskSafetyFlag::CreateDetachedInactive()};
|
||||
bool authentication_required_ = true;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_STUN_REQUEST_H_
|
||||
111
TMessagesProj/jni/voip/webrtc/p2p/base/stun_server.cc
Normal file
111
TMessagesProj/jni/voip/webrtc/p2p/base/stun_server.cc
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/stun_server.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/byte_buffer.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/network/received_packet.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
StunServer::StunServer(rtc::AsyncUDPSocket* socket) : socket_(socket) {
|
||||
socket_->RegisterReceivedPacketCallback(
|
||||
[&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
|
||||
OnPacket(socket, packet);
|
||||
});
|
||||
}
|
||||
|
||||
StunServer::~StunServer() {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
socket_->DeregisterReceivedPacketCallback();
|
||||
}
|
||||
|
||||
void StunServer::OnPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet) {
|
||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||
// Parse the STUN message; eat any messages that fail to parse.
|
||||
rtc::ByteBufferReader bbuf(packet.payload());
|
||||
StunMessage msg;
|
||||
if (!msg.Read(&bbuf)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(?): If unknown non-optional (<= 0x7fff) attributes are found,
|
||||
// send a
|
||||
// 420 "Unknown Attribute" response.
|
||||
|
||||
// Send the message to the appropriate handler function.
|
||||
switch (msg.type()) {
|
||||
case STUN_BINDING_REQUEST:
|
||||
OnBindingRequest(&msg, packet.source_address());
|
||||
break;
|
||||
|
||||
default:
|
||||
SendErrorResponse(msg, packet.source_address(), 600,
|
||||
"Operation Not Supported");
|
||||
}
|
||||
}
|
||||
|
||||
void StunServer::OnBindingRequest(StunMessage* msg,
|
||||
const rtc::SocketAddress& remote_addr) {
|
||||
StunMessage response(STUN_BINDING_RESPONSE, msg->transaction_id());
|
||||
GetStunBindResponse(msg, remote_addr, &response);
|
||||
SendResponse(response, remote_addr);
|
||||
}
|
||||
|
||||
void StunServer::SendErrorResponse(const StunMessage& msg,
|
||||
const rtc::SocketAddress& addr,
|
||||
int error_code,
|
||||
absl::string_view error_desc) {
|
||||
StunMessage err_msg(GetStunErrorResponseType(msg.type()),
|
||||
msg.transaction_id());
|
||||
|
||||
auto err_code = StunAttribute::CreateErrorCode();
|
||||
err_code->SetCode(error_code);
|
||||
err_code->SetReason(std::string(error_desc));
|
||||
err_msg.AddAttribute(std::move(err_code));
|
||||
|
||||
SendResponse(err_msg, addr);
|
||||
}
|
||||
|
||||
void StunServer::SendResponse(const StunMessage& msg,
|
||||
const rtc::SocketAddress& addr) {
|
||||
rtc::ByteBufferWriter buf;
|
||||
msg.Write(&buf);
|
||||
rtc::PacketOptions options;
|
||||
if (socket_->SendTo(buf.Data(), buf.Length(), addr, options) < 0)
|
||||
RTC_LOG_ERR(LS_ERROR) << "sendto";
|
||||
}
|
||||
|
||||
void StunServer::GetStunBindResponse(StunMessage* message,
|
||||
const rtc::SocketAddress& remote_addr,
|
||||
StunMessage* response) const {
|
||||
RTC_DCHECK_EQ(response->type(), STUN_BINDING_RESPONSE);
|
||||
RTC_DCHECK_EQ(response->transaction_id(), message->transaction_id());
|
||||
|
||||
// Tell the user the address that we received their message from.
|
||||
std::unique_ptr<StunAddressAttribute> mapped_addr;
|
||||
if (message->IsLegacy()) {
|
||||
mapped_addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
|
||||
} else {
|
||||
mapped_addr = StunAttribute::CreateXorAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
|
||||
}
|
||||
mapped_addr->SetAddress(remote_addr);
|
||||
response->AddAttribute(std::move(mapped_addr));
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
68
TMessagesProj/jni/voip/webrtc/p2p/base/stun_server.h
Normal file
68
TMessagesProj/jni/voip/webrtc/p2p/base/stun_server.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_STUN_SERVER_H_
|
||||
#define P2P_BASE_STUN_SERVER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/transport/stun.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/async_udp_socket.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
const int STUN_SERVER_PORT = 3478;
|
||||
|
||||
class StunServer {
|
||||
public:
|
||||
// Creates a STUN server, which will listen on the given socket.
|
||||
explicit StunServer(rtc::AsyncUDPSocket* socket);
|
||||
// Removes the STUN server from the socket and deletes the socket.
|
||||
virtual ~StunServer();
|
||||
|
||||
protected:
|
||||
// Callback for packets from socket.
|
||||
void OnPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet);
|
||||
|
||||
// Handlers for the different types of STUN/TURN requests:
|
||||
virtual void OnBindingRequest(StunMessage* msg,
|
||||
const rtc::SocketAddress& addr);
|
||||
void OnAllocateRequest(StunMessage* msg, const rtc::SocketAddress& addr);
|
||||
void OnSharedSecretRequest(StunMessage* msg, const rtc::SocketAddress& addr);
|
||||
void OnSendRequest(StunMessage* msg, const rtc::SocketAddress& addr);
|
||||
|
||||
// Sends an error response to the given message back to the user.
|
||||
void SendErrorResponse(const StunMessage& msg,
|
||||
const rtc::SocketAddress& addr,
|
||||
int error_code,
|
||||
absl::string_view error_desc);
|
||||
|
||||
// Sends the given message to the appropriate destination.
|
||||
void SendResponse(const StunMessage& msg, const rtc::SocketAddress& addr);
|
||||
|
||||
// A helper method to compose a STUN binding response.
|
||||
void GetStunBindResponse(StunMessage* message,
|
||||
const rtc::SocketAddress& remote_addr,
|
||||
StunMessage* response) const;
|
||||
|
||||
private:
|
||||
webrtc::SequenceChecker sequence_checker_;
|
||||
std::unique_ptr<rtc::AsyncUDPSocket> socket_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_STUN_SERVER_H_
|
||||
645
TMessagesProj/jni/voip/webrtc/p2p/base/tcp_port.cc
Normal file
645
TMessagesProj/jni/voip/webrtc/p2p/base/tcp_port.cc
Normal file
|
|
@ -0,0 +1,645 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a diagram of how TCP reconnect works for the active side. The
|
||||
* passive side just waits for an incoming connection.
|
||||
*
|
||||
* - Connected: Indicate whether the TCP socket is connected.
|
||||
*
|
||||
* - Writable: Whether the stun binding is completed. Sending a data packet
|
||||
* before stun binding completed will trigger IPC socket layer to shutdown
|
||||
* the connection.
|
||||
*
|
||||
* - PendingTCP: `connection_pending_` indicates whether there is an
|
||||
* outstanding TCP connection in progress.
|
||||
*
|
||||
* - PretendWri: Tracked by `pretending_to_be_writable_`. Marking connection as
|
||||
* WRITE_TIMEOUT will cause the connection be deleted. Instead, we're
|
||||
* "pretending" we're still writable for a period of time such that reconnect
|
||||
* could work.
|
||||
*
|
||||
* Data could only be sent in state 3. Sening data during state 2 & 6 will get
|
||||
* EWOULDBLOCK, 4 & 5 EPIPE.
|
||||
*
|
||||
* OS Timeout 7 -------------+
|
||||
* +----------------------->|Connected: N |
|
||||
* | |Writable: N | Timeout
|
||||
* | Timeout |Connection is |<----------------+
|
||||
* | +------------------->|Dead | |
|
||||
* | | +--------------+ |
|
||||
* | | ^ |
|
||||
* | | OnClose | |
|
||||
* | | +-----------------------+ | |
|
||||
* | | | | |Timeout |
|
||||
* | | v | | |
|
||||
* | 4 +----------+ 5 -----+--+--+ 6 -----+-----+
|
||||
* | |Connected: N|Send() or |Connected: N| |Connected: Y|
|
||||
* | |Writable: Y|Ping() |Writable: Y|OnConnect |Writable: Y|
|
||||
* | |PendingTCP:N+--------> |PendingTCP:Y+---------> |PendingTCP:N|
|
||||
* | |PretendWri:Y| |PretendWri:Y| |PretendWri:Y|
|
||||
* | +-----+------+ +------------+ +---+--+-----+
|
||||
* | ^ ^ | |
|
||||
* | | | OnClose | |
|
||||
* | | +----------------------------------------------+ |
|
||||
* | | |
|
||||
* | | Stun Binding Completed |
|
||||
* | | |
|
||||
* | | OnClose |
|
||||
* | +------------------------------------------------+ |
|
||||
* | | v
|
||||
* 1 -----------+ 2 -----------+Stun 3 -----------+
|
||||
* |Connected: N| |Connected: Y|Binding |Connected: Y|
|
||||
* |Writable: N|OnConnect |Writable: N|Completed |Writable: Y|
|
||||
* |PendingTCP:Y+---------> |PendingTCP:N+--------> |PendingTCP:N|
|
||||
* |PretendWri:N| |PretendWri:N| |PretendWri:N|
|
||||
* +------------+ +------------+ +------------+
|
||||
*
|
||||
*/
|
||||
|
||||
#include "p2p/base/tcp_port.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/ip_address.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/net_helper.h"
|
||||
#include "rtc_base/network/received_packet.h"
|
||||
#include "rtc_base/rate_tracker.h"
|
||||
#include "rtc_base/thread.h"
|
||||
|
||||
namespace cricket {
|
||||
using ::webrtc::SafeTask;
|
||||
using ::webrtc::TimeDelta;
|
||||
|
||||
TCPPort::TCPPort(rtc::Thread* thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
bool allow_listen,
|
||||
const webrtc::FieldTrialsView* field_trials)
|
||||
: Port(thread,
|
||||
LOCAL_PORT_TYPE,
|
||||
factory,
|
||||
network,
|
||||
min_port,
|
||||
max_port,
|
||||
username,
|
||||
password,
|
||||
field_trials),
|
||||
allow_listen_(allow_listen),
|
||||
error_(0) {
|
||||
// TODO(mallinath) - Set preference value as per RFC 6544.
|
||||
// http://b/issue?id=7141794
|
||||
if (allow_listen_) {
|
||||
TryCreateServerSocket();
|
||||
}
|
||||
// Set TCP_NODELAY (via OPT_NODELAY) for improved performance; this causes
|
||||
// small media packets to be sent immediately rather than being buffered up,
|
||||
// reducing latency.
|
||||
SetOption(rtc::Socket::OPT_NODELAY, 1);
|
||||
}
|
||||
|
||||
TCPPort::~TCPPort() {
|
||||
listen_socket_ = nullptr;
|
||||
std::list<Incoming>::iterator it;
|
||||
for (it = incoming_.begin(); it != incoming_.end(); ++it)
|
||||
delete it->socket;
|
||||
incoming_.clear();
|
||||
}
|
||||
|
||||
Connection* TCPPort::CreateConnection(const Candidate& address,
|
||||
CandidateOrigin origin) {
|
||||
if (!SupportsProtocol(address.protocol())) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((address.tcptype() == TCPTYPE_ACTIVE_STR && !address.is_prflx()) ||
|
||||
(address.tcptype().empty() && address.address().port() == 0)) {
|
||||
// It's active only candidate, we should not try to create connections
|
||||
// for these candidates.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// We can't accept TCP connections incoming on other ports
|
||||
if (origin == ORIGIN_OTHER_PORT)
|
||||
return NULL;
|
||||
|
||||
// We don't know how to act as an ssl server yet
|
||||
if ((address.protocol() == SSLTCP_PROTOCOL_NAME) &&
|
||||
(origin == ORIGIN_THIS_PORT)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!IsCompatibleAddress(address.address())) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TCPConnection* conn = NULL;
|
||||
if (rtc::AsyncPacketSocket* socket = GetIncoming(address.address(), true)) {
|
||||
// Incoming connection; we already created a socket and connected signals,
|
||||
// so we need to hand off the "read packet" responsibility to
|
||||
// TCPConnection.
|
||||
socket->DeregisterReceivedPacketCallback();
|
||||
conn = new TCPConnection(NewWeakPtr(), address, socket);
|
||||
} else {
|
||||
// Outgoing connection, which will create a new socket for which we still
|
||||
// need to connect SignalReadyToSend and SignalSentPacket.
|
||||
conn = new TCPConnection(NewWeakPtr(), address);
|
||||
if (conn->socket()) {
|
||||
conn->socket()->SignalReadyToSend.connect(this, &TCPPort::OnReadyToSend);
|
||||
conn->socket()->SignalSentPacket.connect(this, &TCPPort::OnSentPacket);
|
||||
}
|
||||
}
|
||||
AddOrReplaceConnection(conn);
|
||||
return conn;
|
||||
}
|
||||
|
||||
void TCPPort::PrepareAddress() {
|
||||
if (listen_socket_) {
|
||||
// Socket may be in the CLOSED state if Listen()
|
||||
// failed, we still want to add the socket address.
|
||||
RTC_LOG(LS_VERBOSE) << "Preparing TCP address, current state: "
|
||||
<< static_cast<int>(listen_socket_->GetState());
|
||||
AddAddress(listen_socket_->GetLocalAddress(),
|
||||
listen_socket_->GetLocalAddress(), rtc::SocketAddress(),
|
||||
TCP_PROTOCOL_NAME, "", TCPTYPE_PASSIVE_STR, LOCAL_PORT_TYPE,
|
||||
ICE_TYPE_PREFERENCE_HOST_TCP, 0, "", true);
|
||||
} else {
|
||||
RTC_LOG(LS_INFO) << ToString()
|
||||
<< ": Not listening due to firewall restrictions.";
|
||||
// Note: We still add the address, since otherwise the remote side won't
|
||||
// recognize our incoming TCP connections. According to
|
||||
// https://tools.ietf.org/html/rfc6544#section-4.5, for active candidate,
|
||||
// the port must be set to the discard port, i.e. 9. We can't be 100% sure
|
||||
// which IP address will actually be used, so GetBestIP is as good as we
|
||||
// can do.
|
||||
// TODO(deadbeef): We could do something like create a dummy socket just to
|
||||
// see what IP we get. But that may be overkill.
|
||||
AddAddress(rtc::SocketAddress(Network()->GetBestIP(), DISCARD_PORT),
|
||||
rtc::SocketAddress(Network()->GetBestIP(), 0),
|
||||
rtc::SocketAddress(), TCP_PROTOCOL_NAME, "", TCPTYPE_ACTIVE_STR,
|
||||
LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST_TCP, 0, "", true);
|
||||
}
|
||||
}
|
||||
|
||||
int TCPPort::SendTo(const void* data,
|
||||
size_t size,
|
||||
const rtc::SocketAddress& addr,
|
||||
const rtc::PacketOptions& options,
|
||||
bool payload) {
|
||||
rtc::AsyncPacketSocket* socket = NULL;
|
||||
TCPConnection* conn = static_cast<TCPConnection*>(GetConnection(addr));
|
||||
|
||||
// For Connection, this is the code path used by Ping() to establish
|
||||
// WRITABLE. It has to send through the socket directly as TCPConnection::Send
|
||||
// checks writability.
|
||||
if (conn) {
|
||||
if (!conn->connected()) {
|
||||
conn->MaybeReconnect();
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
socket = conn->socket();
|
||||
if (!socket) {
|
||||
// The failure to initialize should have been logged elsewhere,
|
||||
// so this log is not important.
|
||||
RTC_LOG(LS_INFO) << ToString()
|
||||
<< ": Attempted to send to an uninitialized socket: "
|
||||
<< addr.ToSensitiveString();
|
||||
error_ = EHOSTUNREACH;
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
} else {
|
||||
socket = GetIncoming(addr);
|
||||
if (!socket) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Attempted to send to an unknown destination: "
|
||||
<< addr.ToSensitiveString();
|
||||
error_ = EHOSTUNREACH;
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
}
|
||||
rtc::PacketOptions modified_options(options);
|
||||
CopyPortInformationToPacketInfo(&modified_options.info_signaled_after_sent);
|
||||
int sent = socket->Send(data, size, modified_options);
|
||||
if (sent < 0) {
|
||||
error_ = socket->GetError();
|
||||
// Error from this code path for a Connection (instead of from a bare
|
||||
// socket) will not trigger reconnecting. In theory, this shouldn't matter
|
||||
// as OnClose should always be called and set connected to false.
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": TCP send of " << size
|
||||
<< " bytes failed with error " << error_;
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
int TCPPort::GetOption(rtc::Socket::Option opt, int* value) {
|
||||
auto const& it = socket_options_.find(opt);
|
||||
if (it == socket_options_.end()) {
|
||||
return -1;
|
||||
}
|
||||
*value = it->second;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TCPPort::SetOption(rtc::Socket::Option opt, int value) {
|
||||
socket_options_[opt] = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TCPPort::GetError() {
|
||||
return error_;
|
||||
}
|
||||
|
||||
bool TCPPort::SupportsProtocol(absl::string_view protocol) const {
|
||||
return protocol == TCP_PROTOCOL_NAME || protocol == SSLTCP_PROTOCOL_NAME;
|
||||
}
|
||||
|
||||
ProtocolType TCPPort::GetProtocol() const {
|
||||
return PROTO_TCP;
|
||||
}
|
||||
|
||||
void TCPPort::OnNewConnection(rtc::AsyncListenSocket* socket,
|
||||
rtc::AsyncPacketSocket* new_socket) {
|
||||
RTC_DCHECK_EQ(socket, listen_socket_.get());
|
||||
|
||||
for (const auto& option : socket_options_) {
|
||||
new_socket->SetOption(option.first, option.second);
|
||||
}
|
||||
Incoming incoming;
|
||||
incoming.addr = new_socket->GetRemoteAddress();
|
||||
incoming.socket = new_socket;
|
||||
incoming.socket->RegisterReceivedPacketCallback(
|
||||
[&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
|
||||
OnReadPacket(socket, packet);
|
||||
});
|
||||
incoming.socket->SignalReadyToSend.connect(this, &TCPPort::OnReadyToSend);
|
||||
incoming.socket->SignalSentPacket.connect(this, &TCPPort::OnSentPacket);
|
||||
|
||||
RTC_LOG(LS_VERBOSE) << ToString() << ": Accepted connection from "
|
||||
<< incoming.addr.ToSensitiveString();
|
||||
incoming_.push_back(incoming);
|
||||
}
|
||||
|
||||
void TCPPort::TryCreateServerSocket() {
|
||||
listen_socket_ = absl::WrapUnique(socket_factory()->CreateServerTcpSocket(
|
||||
rtc::SocketAddress(Network()->GetBestIP(), 0), min_port(), max_port(),
|
||||
false /* ssl */));
|
||||
if (!listen_socket_) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< ToString()
|
||||
<< ": TCP server socket creation failed; continuing anyway.";
|
||||
return;
|
||||
}
|
||||
listen_socket_->SignalNewConnection.connect(this, &TCPPort::OnNewConnection);
|
||||
}
|
||||
|
||||
rtc::AsyncPacketSocket* TCPPort::GetIncoming(const rtc::SocketAddress& addr,
|
||||
bool remove) {
|
||||
rtc::AsyncPacketSocket* socket = NULL;
|
||||
for (std::list<Incoming>::iterator it = incoming_.begin();
|
||||
it != incoming_.end(); ++it) {
|
||||
if (it->addr == addr) {
|
||||
socket = it->socket;
|
||||
if (remove)
|
||||
incoming_.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
|
||||
void TCPPort::OnReadPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet) {
|
||||
Port::OnReadPacket(packet, PROTO_TCP);
|
||||
}
|
||||
|
||||
void TCPPort::OnSentPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::SentPacket& sent_packet) {
|
||||
PortInterface::SignalSentPacket(sent_packet);
|
||||
}
|
||||
|
||||
void TCPPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
|
||||
Port::OnReadyToSend();
|
||||
}
|
||||
|
||||
// TODO(qingsi): `CONNECTION_WRITE_CONNECT_TIMEOUT` is overriden by
|
||||
// `ice_unwritable_timeout` in IceConfig when determining the writability state.
|
||||
// Replace this constant with the config parameter assuming the default value if
|
||||
// we decide it is also applicable here.
|
||||
TCPConnection::TCPConnection(rtc::WeakPtr<Port> tcp_port,
|
||||
const Candidate& candidate,
|
||||
rtc::AsyncPacketSocket* socket)
|
||||
: Connection(std::move(tcp_port), 0, candidate),
|
||||
socket_(socket),
|
||||
error_(0),
|
||||
outgoing_(socket == NULL),
|
||||
connection_pending_(false),
|
||||
pretending_to_be_writable_(false),
|
||||
reconnection_timeout_(cricket::CONNECTION_WRITE_CONNECT_TIMEOUT) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
RTC_DCHECK_EQ(port()->GetProtocol(), PROTO_TCP); // Needs to be TCPPort.
|
||||
|
||||
SignalDestroyed.connect(this, &TCPConnection::OnDestroyed);
|
||||
|
||||
if (outgoing_) {
|
||||
CreateOutgoingTcpSocket();
|
||||
} else {
|
||||
// Incoming connections should match one of the network addresses. Same as
|
||||
// what's being checked in OnConnect, but just DCHECKing here.
|
||||
RTC_LOG(LS_VERBOSE) << ToString() << ": socket ipaddr: "
|
||||
<< socket_->GetLocalAddress().ToSensitiveString()
|
||||
<< ", port() Network:" << port()->Network()->ToString();
|
||||
RTC_DCHECK(absl::c_any_of(
|
||||
port_->Network()->GetIPs(), [this](const rtc::InterfaceAddress& addr) {
|
||||
return socket_->GetLocalAddress().ipaddr() == addr;
|
||||
}));
|
||||
ConnectSocketSignals(socket);
|
||||
}
|
||||
}
|
||||
|
||||
TCPConnection::~TCPConnection() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
}
|
||||
|
||||
int TCPConnection::Send(const void* data,
|
||||
size_t size,
|
||||
const rtc::PacketOptions& options) {
|
||||
if (!socket_) {
|
||||
error_ = ENOTCONN;
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
|
||||
// Sending after OnClose on active side will trigger a reconnect for a
|
||||
// outgoing connection. Note that the write state is still WRITABLE as we want
|
||||
// to spend a few seconds attempting a reconnect before saying we're
|
||||
// unwritable.
|
||||
if (!connected()) {
|
||||
MaybeReconnect();
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
|
||||
// Note that this is important to put this after the previous check to give
|
||||
// the connection a chance to reconnect.
|
||||
if (pretending_to_be_writable_ || write_state() != STATE_WRITABLE) {
|
||||
// TODO(?): Should STATE_WRITE_TIMEOUT return a non-blocking error?
|
||||
error_ = ENOTCONN;
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
stats_.sent_total_packets++;
|
||||
rtc::PacketOptions modified_options(options);
|
||||
tcp_port()->CopyPortInformationToPacketInfo(
|
||||
&modified_options.info_signaled_after_sent);
|
||||
int sent = socket_->Send(data, size, modified_options);
|
||||
int64_t now = rtc::TimeMillis();
|
||||
if (sent < 0) {
|
||||
stats_.sent_discarded_packets++;
|
||||
error_ = socket_->GetError();
|
||||
} else {
|
||||
send_rate_tracker_.AddSamplesAtTime(now, sent);
|
||||
}
|
||||
last_send_data_ = now;
|
||||
return sent;
|
||||
}
|
||||
|
||||
int TCPConnection::GetError() {
|
||||
return error_;
|
||||
}
|
||||
|
||||
void TCPConnection::OnConnectionRequestResponse(StunRequest* req,
|
||||
StunMessage* response) {
|
||||
// Process the STUN response before we inform upper layer ready to send.
|
||||
Connection::OnConnectionRequestResponse(req, response);
|
||||
|
||||
// If we're in the state of pretending to be writeable, we should inform the
|
||||
// upper layer it's ready to send again as previous EWOULDLBLOCK from socket
|
||||
// would have stopped the outgoing stream.
|
||||
if (pretending_to_be_writable_) {
|
||||
Connection::OnReadyToSend();
|
||||
}
|
||||
pretending_to_be_writable_ = false;
|
||||
RTC_DCHECK(write_state() == STATE_WRITABLE);
|
||||
}
|
||||
|
||||
void TCPConnection::OnConnect(rtc::AsyncPacketSocket* socket) {
|
||||
RTC_DCHECK_EQ(socket, socket_.get());
|
||||
|
||||
if (!port_) {
|
||||
RTC_LOG(LS_ERROR) << "TCPConnection: Port has been deleted.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not use this port if the socket bound to an address not associated with
|
||||
// the desired network interface. This is seen in Chrome, where TCP sockets
|
||||
// cannot be given a binding address, and the platform is expected to pick
|
||||
// the correct local address.
|
||||
//
|
||||
// However, there are two situations in which we allow the bound address to
|
||||
// not be one of the addresses of the requested interface:
|
||||
// 1. The bound address is the loopback address. This happens when a proxy
|
||||
// forces TCP to bind to only the localhost address (see issue 3927).
|
||||
// 2. The bound address is the "any address". This happens when
|
||||
// multiple_routes is disabled (see issue 4780).
|
||||
//
|
||||
// Note that, aside from minor differences in log statements, this logic is
|
||||
// identical to that in TurnPort.
|
||||
const rtc::SocketAddress& socket_address = socket->GetLocalAddress();
|
||||
if (absl::c_any_of(port_->Network()->GetIPs(),
|
||||
[socket_address](const rtc::InterfaceAddress& addr) {
|
||||
return socket_address.ipaddr() == addr;
|
||||
})) {
|
||||
RTC_LOG(LS_VERBOSE) << ToString() << ": Connection established to "
|
||||
<< socket->GetRemoteAddress().ToSensitiveString();
|
||||
} else {
|
||||
if (socket->GetLocalAddress().IsLoopbackIP()) {
|
||||
RTC_LOG(LS_WARNING) << "Socket is bound to the address:"
|
||||
<< socket_address.ipaddr().ToSensitiveString()
|
||||
<< ", rather than an address associated with network:"
|
||||
<< port_->Network()->ToString()
|
||||
<< ". Still allowing it since it's localhost.";
|
||||
} else if (IPIsAny(port_->Network()->GetBestIP())) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "Socket is bound to the address:"
|
||||
<< socket_address.ipaddr().ToSensitiveString()
|
||||
<< ", rather than an address associated with network:"
|
||||
<< port_->Network()->ToString()
|
||||
<< ". Still allowing it since it's the 'any' address"
|
||||
", possibly caused by multiple_routes being disabled.";
|
||||
} else {
|
||||
RTC_LOG(LS_WARNING) << "Dropping connection as TCP socket bound to IP "
|
||||
<< socket_address.ipaddr().ToSensitiveString()
|
||||
<< ", rather than an address associated with network:"
|
||||
<< port_->Network()->ToString();
|
||||
OnClose(socket, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Connection is established successfully.
|
||||
set_connected(true);
|
||||
connection_pending_ = false;
|
||||
}
|
||||
|
||||
void TCPConnection::OnClose(rtc::AsyncPacketSocket* socket, int error) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
RTC_DCHECK_EQ(socket, socket_.get());
|
||||
RTC_LOG(LS_INFO) << ToString() << ": Connection closed with error " << error;
|
||||
|
||||
if (!port_) {
|
||||
RTC_LOG(LS_ERROR) << "TCPConnection: Port has been deleted.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Guard against the condition where IPC socket will call OnClose for every
|
||||
// packet it can't send.
|
||||
if (connected()) {
|
||||
set_connected(false);
|
||||
|
||||
// Prevent the connection from being destroyed by redundant SignalClose
|
||||
// events.
|
||||
pretending_to_be_writable_ = true;
|
||||
|
||||
// If this connection can't become connected and writable again in 5
|
||||
// seconds, it's time to tear this down. This is the case for the original
|
||||
// TCP connection on passive side during a reconnect.
|
||||
// We don't attempt reconnect right here. This is to avoid a case where the
|
||||
// shutdown is intentional and reconnect is not necessary. We only reconnect
|
||||
// when the connection is used to Send() or Ping().
|
||||
network_thread()->PostDelayedTask(
|
||||
SafeTask(network_safety_.flag(),
|
||||
[this]() {
|
||||
if (pretending_to_be_writable_) {
|
||||
Destroy();
|
||||
}
|
||||
}),
|
||||
TimeDelta::Millis(reconnection_timeout()));
|
||||
} else if (!pretending_to_be_writable_) {
|
||||
// OnClose could be called when the underneath socket times out during the
|
||||
// initial connect() (i.e. `pretending_to_be_writable_` is false) . We have
|
||||
// to manually destroy here as this connection, as never connected, will not
|
||||
// be scheduled for ping to trigger destroy.
|
||||
DisconnectSocketSignals(socket_.get());
|
||||
port()->DestroyConnectionAsync(this);
|
||||
}
|
||||
}
|
||||
|
||||
void TCPConnection::MaybeReconnect() {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
// Only reconnect for an outgoing TCPConnection when OnClose was signaled and
|
||||
// no outstanding reconnect is pending.
|
||||
if (connected() || connection_pending_ || !outgoing_) {
|
||||
return;
|
||||
}
|
||||
|
||||
RTC_LOG(LS_INFO) << ToString()
|
||||
<< ": TCP Connection with remote is closed, "
|
||||
"trying to reconnect";
|
||||
|
||||
CreateOutgoingTcpSocket();
|
||||
error_ = EPIPE;
|
||||
}
|
||||
|
||||
void TCPConnection::OnReadPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
RTC_DCHECK_EQ(socket, socket_.get());
|
||||
Connection::OnReadPacket(packet);
|
||||
}
|
||||
|
||||
void TCPConnection::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
RTC_DCHECK_EQ(socket, socket_.get());
|
||||
Connection::OnReadyToSend();
|
||||
}
|
||||
|
||||
void TCPConnection::OnDestroyed(Connection* c) {
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
RTC_DCHECK_EQ(c, this);
|
||||
if (socket_) {
|
||||
DisconnectSocketSignals(socket_.get());
|
||||
}
|
||||
}
|
||||
|
||||
void TCPConnection::CreateOutgoingTcpSocket() {
|
||||
RTC_DCHECK(outgoing_);
|
||||
int opts = (remote_candidate().protocol() == SSLTCP_PROTOCOL_NAME)
|
||||
? rtc::PacketSocketFactory::OPT_TLS_FAKE
|
||||
: 0;
|
||||
|
||||
if (socket_) {
|
||||
DisconnectSocketSignals(socket_.get());
|
||||
}
|
||||
|
||||
rtc::PacketSocketTcpOptions tcp_opts;
|
||||
tcp_opts.opts = opts;
|
||||
socket_.reset(port()->socket_factory()->CreateClientTcpSocket(
|
||||
rtc::SocketAddress(port()->Network()->GetBestIP(), 0),
|
||||
remote_candidate().address(), port()->proxy(), port()->user_agent(),
|
||||
tcp_opts));
|
||||
if (socket_) {
|
||||
RTC_LOG(LS_VERBOSE) << ToString() << ": Connecting from "
|
||||
<< socket_->GetLocalAddress().ToSensitiveString()
|
||||
<< " to "
|
||||
<< remote_candidate().address().ToSensitiveString();
|
||||
set_connected(false);
|
||||
connection_pending_ = true;
|
||||
ConnectSocketSignals(socket_.get());
|
||||
} else {
|
||||
RTC_LOG(LS_WARNING) << ToString() << ": Failed to create connection to "
|
||||
<< remote_candidate().address().ToSensitiveString();
|
||||
set_state(IceCandidatePairState::FAILED);
|
||||
// We can't FailAndPrune directly here. FailAndPrune and deletes all
|
||||
// the StunRequests from the request_map_. And if this is in the stack
|
||||
// of Connection::Ping(), we are still using the request.
|
||||
// Unwind the stack and defer the FailAndPrune.
|
||||
network_thread()->PostTask(
|
||||
SafeTask(network_safety_.flag(), [this]() { FailAndPrune(); }));
|
||||
}
|
||||
}
|
||||
|
||||
void TCPConnection::ConnectSocketSignals(rtc::AsyncPacketSocket* socket) {
|
||||
if (outgoing_) {
|
||||
socket->SignalConnect.connect(this, &TCPConnection::OnConnect);
|
||||
}
|
||||
socket->RegisterReceivedPacketCallback(
|
||||
[&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
|
||||
OnReadPacket(socket, packet);
|
||||
});
|
||||
socket->SignalReadyToSend.connect(this, &TCPConnection::OnReadyToSend);
|
||||
socket->SubscribeCloseEvent(this, [this, safety = network_safety_.flag()](
|
||||
rtc::AsyncPacketSocket* s, int err) {
|
||||
if (safety->alive())
|
||||
OnClose(s, err);
|
||||
});
|
||||
}
|
||||
|
||||
void TCPConnection::DisconnectSocketSignals(rtc::AsyncPacketSocket* socket) {
|
||||
if (outgoing_) {
|
||||
socket->SignalConnect.disconnect(this);
|
||||
}
|
||||
socket->DeregisterReceivedPacketCallback();
|
||||
socket->SignalReadyToSend.disconnect(this);
|
||||
socket->UnsubscribeCloseEvent(this);
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
203
TMessagesProj/jni/voip/webrtc/p2p/base/tcp_port.h
Normal file
203
TMessagesProj/jni/voip/webrtc/p2p/base/tcp_port.h
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_TCP_PORT_H_
|
||||
#define P2P_BASE_TCP_PORT_H_
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "p2p/base/connection.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/containers/flat_map.h"
|
||||
#include "rtc_base/network/received_packet.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class TCPConnection;
|
||||
|
||||
// Communicates using a local TCP port.
|
||||
//
|
||||
// This class is designed to allow subclasses to take advantage of the
|
||||
// connection management provided by this class. A subclass should take of all
|
||||
// packet sending and preparation, but when a packet is received, it should
|
||||
// call this TCPPort::OnReadPacket (3 arg) to dispatch to a connection.
|
||||
class TCPPort : public Port {
|
||||
public:
|
||||
static std::unique_ptr<TCPPort> Create(
|
||||
rtc::Thread* thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
bool allow_listen,
|
||||
const webrtc::FieldTrialsView* field_trials = nullptr) {
|
||||
// Using `new` to access a non-public constructor.
|
||||
return absl::WrapUnique(new TCPPort(thread, factory, network, min_port,
|
||||
max_port, username, password,
|
||||
allow_listen, field_trials));
|
||||
}
|
||||
~TCPPort() override;
|
||||
|
||||
Connection* CreateConnection(const Candidate& address,
|
||||
CandidateOrigin origin) override;
|
||||
|
||||
void PrepareAddress() override;
|
||||
|
||||
// Options apply to accepted sockets.
|
||||
// TODO(bugs.webrtc.org/13065): Apply also to outgoing and existing
|
||||
// connections.
|
||||
int GetOption(rtc::Socket::Option opt, int* value) override;
|
||||
int SetOption(rtc::Socket::Option opt, int value) override;
|
||||
int GetError() override;
|
||||
bool SupportsProtocol(absl::string_view protocol) const override;
|
||||
ProtocolType GetProtocol() const override;
|
||||
|
||||
protected:
|
||||
TCPPort(rtc::Thread* thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
bool allow_listen,
|
||||
const webrtc::FieldTrialsView* field_trials);
|
||||
|
||||
// Handles sending using the local TCP socket.
|
||||
int SendTo(const void* data,
|
||||
size_t size,
|
||||
const rtc::SocketAddress& addr,
|
||||
const rtc::PacketOptions& options,
|
||||
bool payload) override;
|
||||
|
||||
// Accepts incoming TCP connection.
|
||||
void OnNewConnection(rtc::AsyncListenSocket* socket,
|
||||
rtc::AsyncPacketSocket* new_socket);
|
||||
|
||||
private:
|
||||
struct Incoming {
|
||||
rtc::SocketAddress addr;
|
||||
rtc::AsyncPacketSocket* socket;
|
||||
};
|
||||
|
||||
void TryCreateServerSocket();
|
||||
|
||||
rtc::AsyncPacketSocket* GetIncoming(const rtc::SocketAddress& addr,
|
||||
bool remove = false);
|
||||
|
||||
// Receives packet signal from the local TCP Socket.
|
||||
void OnReadPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet);
|
||||
|
||||
void OnSentPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::SentPacket& sent_packet) override;
|
||||
|
||||
void OnReadyToSend(rtc::AsyncPacketSocket* socket);
|
||||
|
||||
bool allow_listen_;
|
||||
std::unique_ptr<rtc::AsyncListenSocket> listen_socket_;
|
||||
// Options to be applied to accepted sockets.
|
||||
// TODO(bugs.webrtc:13065): Configure connect/accept in the same way, but
|
||||
// currently, setting OPT_NODELAY for client sockets is done (unconditionally)
|
||||
// by BasicPacketSocketFactory::CreateClientTcpSocket.
|
||||
webrtc::flat_map<rtc::Socket::Option, int> socket_options_;
|
||||
|
||||
int error_;
|
||||
std::list<Incoming> incoming_;
|
||||
|
||||
friend class TCPConnection;
|
||||
};
|
||||
|
||||
class TCPConnection : public Connection, public sigslot::has_slots<> {
|
||||
public:
|
||||
// Connection is outgoing unless socket is specified
|
||||
TCPConnection(rtc::WeakPtr<Port> tcp_port,
|
||||
const Candidate& candidate,
|
||||
rtc::AsyncPacketSocket* socket = nullptr);
|
||||
~TCPConnection() override;
|
||||
|
||||
int Send(const void* data,
|
||||
size_t size,
|
||||
const rtc::PacketOptions& options) override;
|
||||
int GetError() override;
|
||||
|
||||
rtc::AsyncPacketSocket* socket() { return socket_.get(); }
|
||||
|
||||
// Allow test cases to overwrite the default timeout period.
|
||||
int reconnection_timeout() const { return reconnection_timeout_; }
|
||||
void set_reconnection_timeout(int timeout_in_ms) {
|
||||
reconnection_timeout_ = timeout_in_ms;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Set waiting_for_stun_binding_complete_ to false to allow data packets in
|
||||
// addition to what Port::OnConnectionRequestResponse does.
|
||||
void OnConnectionRequestResponse(StunRequest* req,
|
||||
StunMessage* response) override;
|
||||
|
||||
private:
|
||||
friend class TCPPort; // For `MaybeReconnect()`.
|
||||
|
||||
// Helper function to handle the case when Ping or Send fails with error
|
||||
// related to socket close.
|
||||
void MaybeReconnect();
|
||||
|
||||
void CreateOutgoingTcpSocket() RTC_RUN_ON(network_thread());
|
||||
|
||||
void ConnectSocketSignals(rtc::AsyncPacketSocket* socket)
|
||||
RTC_RUN_ON(network_thread());
|
||||
|
||||
void DisconnectSocketSignals(rtc::AsyncPacketSocket* socket)
|
||||
RTC_RUN_ON(network_thread());
|
||||
|
||||
void OnConnect(rtc::AsyncPacketSocket* socket);
|
||||
void OnClose(rtc::AsyncPacketSocket* socket, int error);
|
||||
void OnReadPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet);
|
||||
void OnReadyToSend(rtc::AsyncPacketSocket* socket);
|
||||
void OnDestroyed(Connection* c);
|
||||
|
||||
TCPPort* tcp_port() {
|
||||
RTC_DCHECK_EQ(port()->GetProtocol(), PROTO_TCP);
|
||||
return static_cast<TCPPort*>(port());
|
||||
}
|
||||
|
||||
std::unique_ptr<rtc::AsyncPacketSocket> socket_;
|
||||
int error_;
|
||||
const bool outgoing_;
|
||||
|
||||
// Guard against multiple outgoing tcp connection during a reconnect.
|
||||
bool connection_pending_;
|
||||
|
||||
// Guard against data packets sent when we reconnect a TCP connection. During
|
||||
// reconnecting, when a new tcp connection has being made, we can't send data
|
||||
// packets out until the STUN binding is completed (i.e. the write state is
|
||||
// set to WRITABLE again by Connection::OnConnectionRequestResponse). IPC
|
||||
// socket, when receiving data packets before that, will trigger OnError which
|
||||
// will terminate the newly created connection.
|
||||
bool pretending_to_be_writable_;
|
||||
|
||||
// Allow test case to overwrite the default timeout period.
|
||||
int reconnection_timeout_;
|
||||
|
||||
webrtc::ScopedTaskSafety network_safety_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_TCP_PORT_H_
|
||||
48
TMessagesProj/jni/voip/webrtc/p2p/base/test_stun_server.cc
Normal file
48
TMessagesProj/jni/voip/webrtc/p2p/base/test_stun_server.cc
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2017 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/test_stun_server.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rtc_base/socket.h"
|
||||
#include "rtc_base/socket_server.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
std::unique_ptr<TestStunServer, std::function<void(TestStunServer*)>>
|
||||
TestStunServer::Create(rtc::SocketServer* ss,
|
||||
const rtc::SocketAddress& addr,
|
||||
rtc::Thread& network_thread) {
|
||||
rtc::Socket* socket = ss->CreateSocket(addr.family(), SOCK_DGRAM);
|
||||
rtc::AsyncUDPSocket* udp_socket = rtc::AsyncUDPSocket::Create(socket, addr);
|
||||
TestStunServer* server = nullptr;
|
||||
network_thread.BlockingCall(
|
||||
[&]() { server = new TestStunServer(udp_socket, network_thread); });
|
||||
std::unique_ptr<TestStunServer, std::function<void(TestStunServer*)>> result(
|
||||
server, [&](TestStunServer* server) {
|
||||
network_thread.BlockingCall([server]() { delete server; });
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void TestStunServer::OnBindingRequest(StunMessage* msg,
|
||||
const rtc::SocketAddress& remote_addr) {
|
||||
RTC_DCHECK_RUN_ON(&network_thread_);
|
||||
if (fake_stun_addr_.IsNil()) {
|
||||
StunServer::OnBindingRequest(msg, remote_addr);
|
||||
} else {
|
||||
StunMessage response(STUN_BINDING_RESPONSE, msg->transaction_id());
|
||||
GetStunBindResponse(msg, fake_stun_addr_, &response);
|
||||
SendResponse(response, remote_addr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
55
TMessagesProj/jni/voip/webrtc/p2p/base/test_stun_server.h
Normal file
55
TMessagesProj/jni/voip/webrtc/p2p/base/test_stun_server.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2008 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_TEST_STUN_SERVER_H_
|
||||
#define P2P_BASE_TEST_STUN_SERVER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/transport/stun.h"
|
||||
#include "p2p/base/stun_server.h"
|
||||
#include "rtc_base/async_udp_socket.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/socket_server.h"
|
||||
#include "rtc_base/thread.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// A test STUN server. Useful for unit tests.
|
||||
class TestStunServer : StunServer {
|
||||
public:
|
||||
using StunServerPtr =
|
||||
std::unique_ptr<TestStunServer, std::function<void(TestStunServer*)>>;
|
||||
static StunServerPtr Create(rtc::SocketServer* ss,
|
||||
const rtc::SocketAddress& addr,
|
||||
rtc::Thread& network_thread);
|
||||
|
||||
// Set a fake STUN address to return to the client.
|
||||
void set_fake_stun_addr(const rtc::SocketAddress& addr) {
|
||||
fake_stun_addr_ = addr;
|
||||
}
|
||||
|
||||
private:
|
||||
static void DeleteOnNetworkThread(TestStunServer* server);
|
||||
|
||||
TestStunServer(rtc::AsyncUDPSocket* socket, rtc::Thread& network_thread)
|
||||
: StunServer(socket), network_thread_(network_thread) {}
|
||||
|
||||
void OnBindingRequest(StunMessage* msg,
|
||||
const rtc::SocketAddress& remote_addr) override;
|
||||
|
||||
private:
|
||||
rtc::SocketAddress fake_stun_addr_;
|
||||
rtc::Thread& network_thread_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_TEST_STUN_SERVER_H_
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2017 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_TEST_TURN_CUSTOMIZER_H_
|
||||
#define P2P_BASE_TEST_TURN_CUSTOMIZER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/turn_customizer.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class TestTurnCustomizer : public webrtc::TurnCustomizer {
|
||||
public:
|
||||
TestTurnCustomizer() {}
|
||||
virtual ~TestTurnCustomizer() {}
|
||||
|
||||
enum TestTurnAttributeExtensions {
|
||||
// Test only attribute
|
||||
STUN_ATTR_COUNTER = 0xFF02 // Number
|
||||
};
|
||||
|
||||
void MaybeModifyOutgoingStunMessage(cricket::PortInterface* port,
|
||||
cricket::StunMessage* message) override {
|
||||
modify_cnt_++;
|
||||
|
||||
ASSERT_NE(0, message->type());
|
||||
if (add_counter_) {
|
||||
message->AddAttribute(std::make_unique<cricket::StunUInt32Attribute>(
|
||||
STUN_ATTR_COUNTER, modify_cnt_));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool AllowChannelData(cricket::PortInterface* port,
|
||||
const void* data,
|
||||
size_t size,
|
||||
bool payload) override {
|
||||
allow_channel_data_cnt_++;
|
||||
return allow_channel_data_;
|
||||
}
|
||||
|
||||
bool add_counter_ = false;
|
||||
bool allow_channel_data_ = true;
|
||||
unsigned int modify_cnt_ = 0;
|
||||
unsigned int allow_channel_data_cnt_ = 0;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_TEST_TURN_CUSTOMIZER_H_
|
||||
161
TMessagesProj/jni/voip/webrtc/p2p/base/test_turn_server.h
Normal file
161
TMessagesProj/jni/voip/webrtc/p2p/base/test_turn_server.h
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright 2012 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_TEST_TURN_SERVER_H_
|
||||
#define P2P_BASE_TEST_TURN_SERVER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/transport/stun.h"
|
||||
#include "p2p/base/basic_packet_socket_factory.h"
|
||||
#include "p2p/base/turn_server.h"
|
||||
#include "rtc_base/async_udp_socket.h"
|
||||
#include "rtc_base/ssl_adapter.h"
|
||||
#include "rtc_base/ssl_identity.h"
|
||||
#include "rtc_base/thread.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
static const char kTestRealm[] = "example.org";
|
||||
static const char kTestSoftware[] = "TestTurnServer";
|
||||
|
||||
class TestTurnRedirector : public TurnRedirectInterface {
|
||||
public:
|
||||
explicit TestTurnRedirector(const std::vector<rtc::SocketAddress>& addresses)
|
||||
: alternate_server_addresses_(addresses),
|
||||
iter_(alternate_server_addresses_.begin()) {}
|
||||
|
||||
virtual bool ShouldRedirect(const rtc::SocketAddress&,
|
||||
rtc::SocketAddress* out) {
|
||||
if (!out || iter_ == alternate_server_addresses_.end()) {
|
||||
return false;
|
||||
}
|
||||
*out = *iter_++;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<rtc::SocketAddress>& alternate_server_addresses_;
|
||||
std::vector<rtc::SocketAddress>::const_iterator iter_;
|
||||
};
|
||||
|
||||
class TestTurnServer : public TurnAuthInterface {
|
||||
public:
|
||||
TestTurnServer(rtc::Thread* thread,
|
||||
rtc::SocketFactory* socket_factory,
|
||||
const rtc::SocketAddress& int_addr,
|
||||
const rtc::SocketAddress& udp_ext_addr,
|
||||
ProtocolType int_protocol = PROTO_UDP,
|
||||
bool ignore_bad_cert = true,
|
||||
absl::string_view common_name = "test turn server")
|
||||
: server_(thread), socket_factory_(socket_factory) {
|
||||
AddInternalSocket(int_addr, int_protocol, ignore_bad_cert, common_name);
|
||||
server_.SetExternalSocketFactory(
|
||||
new rtc::BasicPacketSocketFactory(socket_factory), udp_ext_addr);
|
||||
server_.set_realm(kTestRealm);
|
||||
server_.set_software(kTestSoftware);
|
||||
server_.set_auth_hook(this);
|
||||
}
|
||||
|
||||
~TestTurnServer() { RTC_DCHECK(thread_checker_.IsCurrent()); }
|
||||
|
||||
void set_enable_otu_nonce(bool enable) {
|
||||
RTC_DCHECK(thread_checker_.IsCurrent());
|
||||
server_.set_enable_otu_nonce(enable);
|
||||
}
|
||||
|
||||
TurnServer* server() {
|
||||
RTC_DCHECK(thread_checker_.IsCurrent());
|
||||
return &server_;
|
||||
}
|
||||
|
||||
void set_redirect_hook(TurnRedirectInterface* redirect_hook) {
|
||||
RTC_DCHECK(thread_checker_.IsCurrent());
|
||||
server_.set_redirect_hook(redirect_hook);
|
||||
}
|
||||
|
||||
void set_enable_permission_checks(bool enable) {
|
||||
RTC_DCHECK(thread_checker_.IsCurrent());
|
||||
server_.set_enable_permission_checks(enable);
|
||||
}
|
||||
|
||||
void AddInternalSocket(const rtc::SocketAddress& int_addr,
|
||||
ProtocolType proto,
|
||||
bool ignore_bad_cert = true,
|
||||
absl::string_view common_name = "test turn server") {
|
||||
RTC_DCHECK(thread_checker_.IsCurrent());
|
||||
if (proto == cricket::PROTO_UDP) {
|
||||
server_.AddInternalSocket(
|
||||
rtc::AsyncUDPSocket::Create(socket_factory_, int_addr), proto);
|
||||
} else if (proto == cricket::PROTO_TCP || proto == cricket::PROTO_TLS) {
|
||||
// For TCP we need to create a server socket which can listen for incoming
|
||||
// new connections.
|
||||
rtc::Socket* socket = socket_factory_->CreateSocket(AF_INET, SOCK_STREAM);
|
||||
socket->Bind(int_addr);
|
||||
socket->Listen(5);
|
||||
if (proto == cricket::PROTO_TLS) {
|
||||
// For TLS, wrap the TCP socket with an SSL adapter. The adapter must
|
||||
// be configured with a self-signed certificate for testing.
|
||||
// Additionally, the client will not present a valid certificate, so we
|
||||
// must not fail when checking the peer's identity.
|
||||
std::unique_ptr<rtc::SSLAdapterFactory> ssl_adapter_factory =
|
||||
rtc::SSLAdapterFactory::Create();
|
||||
ssl_adapter_factory->SetRole(rtc::SSL_SERVER);
|
||||
ssl_adapter_factory->SetIdentity(
|
||||
rtc::SSLIdentity::Create(common_name, rtc::KeyParams()));
|
||||
ssl_adapter_factory->SetIgnoreBadCert(ignore_bad_cert);
|
||||
server_.AddInternalServerSocket(socket, proto,
|
||||
std::move(ssl_adapter_factory));
|
||||
} else {
|
||||
server_.AddInternalServerSocket(socket, proto);
|
||||
}
|
||||
} else {
|
||||
RTC_DCHECK_NOTREACHED() << "Unknown protocol type: " << proto;
|
||||
}
|
||||
}
|
||||
|
||||
// Finds the first allocation in the server allocation map with a source
|
||||
// ip and port matching the socket address provided.
|
||||
TurnServerAllocation* FindAllocation(const rtc::SocketAddress& src) {
|
||||
RTC_DCHECK(thread_checker_.IsCurrent());
|
||||
const TurnServer::AllocationMap& map = server_.allocations();
|
||||
for (TurnServer::AllocationMap::const_iterator it = map.begin();
|
||||
it != map.end(); ++it) {
|
||||
if (src == it->first.src()) {
|
||||
return it->second.get();
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
// For this test server, succeed if the password is the same as the username.
|
||||
// Obviously, do not use this in a production environment.
|
||||
virtual bool GetKey(absl::string_view username,
|
||||
absl::string_view realm,
|
||||
std::string* key) {
|
||||
RTC_DCHECK(thread_checker_.IsCurrent());
|
||||
return ComputeStunCredentialHash(std::string(username), std::string(realm),
|
||||
std::string(username), key);
|
||||
}
|
||||
|
||||
TurnServer server_;
|
||||
rtc::SocketFactory* socket_factory_;
|
||||
webrtc::SequenceChecker thread_checker_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_TEST_TURN_SERVER_H_
|
||||
196
TMessagesProj/jni/voip/webrtc/p2p/base/transport_description.cc
Normal file
196
TMessagesProj/jni/voip/webrtc/p2p/base/transport_description.cc
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* Copyright 2013 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/transport_description.h"
|
||||
|
||||
#include "absl/strings/ascii.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "rtc_base/arraysize.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
|
||||
using webrtc::RTCError;
|
||||
using webrtc::RTCErrorOr;
|
||||
using webrtc::RTCErrorType;
|
||||
|
||||
namespace cricket {
|
||||
namespace {
|
||||
|
||||
bool IsIceChar(char c) {
|
||||
// Note: '-', '=', '#' and '_' are *not* valid ice-chars but temporarily
|
||||
// permitted in order to allow external software to upgrade.
|
||||
if (c == '-' || c == '=' || c == '#' || c == '_') {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "'-', '=', '#' and '-' are not valid ice-char and thus not "
|
||||
<< "permitted in ufrag or pwd. This is a protocol violation that "
|
||||
<< "is permitted to allow upgrading but will be rejected in "
|
||||
<< "the future. See https://crbug.com/1053756";
|
||||
return true;
|
||||
}
|
||||
return absl::ascii_isalnum(c) || c == '+' || c == '/';
|
||||
}
|
||||
|
||||
RTCError ValidateIceUfrag(absl::string_view raw_ufrag) {
|
||||
if (!(ICE_UFRAG_MIN_LENGTH <= raw_ufrag.size() &&
|
||||
raw_ufrag.size() <= ICE_UFRAG_MAX_LENGTH)) {
|
||||
rtc::StringBuilder sb;
|
||||
sb << "ICE ufrag must be between " << ICE_UFRAG_MIN_LENGTH << " and "
|
||||
<< ICE_UFRAG_MAX_LENGTH << " characters long.";
|
||||
return RTCError(RTCErrorType::SYNTAX_ERROR, sb.Release());
|
||||
}
|
||||
|
||||
if (!absl::c_all_of(raw_ufrag, IsIceChar)) {
|
||||
return RTCError(
|
||||
RTCErrorType::SYNTAX_ERROR,
|
||||
"ICE ufrag must contain only alphanumeric characters, '+', and '/'.");
|
||||
}
|
||||
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
RTCError ValidateIcePwd(absl::string_view raw_pwd) {
|
||||
if (!(ICE_PWD_MIN_LENGTH <= raw_pwd.size() &&
|
||||
raw_pwd.size() <= ICE_PWD_MAX_LENGTH)) {
|
||||
rtc::StringBuilder sb;
|
||||
sb << "ICE pwd must be between " << ICE_PWD_MIN_LENGTH << " and "
|
||||
<< ICE_PWD_MAX_LENGTH << " characters long.";
|
||||
return RTCError(RTCErrorType::SYNTAX_ERROR, sb.Release());
|
||||
}
|
||||
|
||||
if (!absl::c_all_of(raw_pwd, IsIceChar)) {
|
||||
return RTCError(
|
||||
RTCErrorType::SYNTAX_ERROR,
|
||||
"ICE pwd must contain only alphanumeric characters, '+', and '/'.");
|
||||
}
|
||||
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RTCErrorOr<IceParameters> IceParameters::Parse(absl::string_view raw_ufrag,
|
||||
absl::string_view raw_pwd) {
|
||||
IceParameters parameters(std::string(raw_ufrag), std::string(raw_pwd),
|
||||
/* renomination= */ false);
|
||||
auto result = parameters.Validate();
|
||||
if (!result.ok()) {
|
||||
return result;
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
RTCError IceParameters::Validate() const {
|
||||
// For legacy protocols.
|
||||
// TODO(zhihuang): Remove this once the legacy protocol is no longer
|
||||
// supported.
|
||||
if (ufrag.empty() && pwd.empty()) {
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
auto ufrag_result = ValidateIceUfrag(ufrag);
|
||||
if (!ufrag_result.ok()) {
|
||||
return ufrag_result;
|
||||
}
|
||||
|
||||
auto pwd_result = ValidateIcePwd(pwd);
|
||||
if (!pwd_result.ok()) {
|
||||
return pwd_result;
|
||||
}
|
||||
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
absl::optional<ConnectionRole> StringToConnectionRole(
|
||||
absl::string_view role_str) {
|
||||
const char* const roles[] = {
|
||||
CONNECTIONROLE_ACTIVE_STR, CONNECTIONROLE_PASSIVE_STR,
|
||||
CONNECTIONROLE_ACTPASS_STR, CONNECTIONROLE_HOLDCONN_STR};
|
||||
|
||||
for (size_t i = 0; i < arraysize(roles); ++i) {
|
||||
if (absl::EqualsIgnoreCase(roles[i], role_str)) {
|
||||
return static_cast<ConnectionRole>(CONNECTIONROLE_ACTIVE + i);
|
||||
}
|
||||
}
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
bool ConnectionRoleToString(const ConnectionRole& role, std::string* role_str) {
|
||||
switch (role) {
|
||||
case cricket::CONNECTIONROLE_ACTIVE:
|
||||
*role_str = cricket::CONNECTIONROLE_ACTIVE_STR;
|
||||
break;
|
||||
case cricket::CONNECTIONROLE_ACTPASS:
|
||||
*role_str = cricket::CONNECTIONROLE_ACTPASS_STR;
|
||||
break;
|
||||
case cricket::CONNECTIONROLE_PASSIVE:
|
||||
*role_str = cricket::CONNECTIONROLE_PASSIVE_STR;
|
||||
break;
|
||||
case cricket::CONNECTIONROLE_HOLDCONN:
|
||||
*role_str = cricket::CONNECTIONROLE_HOLDCONN_STR;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TransportDescription::TransportDescription()
|
||||
: ice_mode(ICEMODE_FULL), connection_role(CONNECTIONROLE_NONE) {}
|
||||
|
||||
TransportDescription::TransportDescription(
|
||||
const std::vector<std::string>& transport_options,
|
||||
absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd,
|
||||
IceMode ice_mode,
|
||||
ConnectionRole role,
|
||||
const rtc::SSLFingerprint* identity_fingerprint)
|
||||
: transport_options(transport_options),
|
||||
ice_ufrag(ice_ufrag),
|
||||
ice_pwd(ice_pwd),
|
||||
ice_mode(ice_mode),
|
||||
connection_role(role),
|
||||
identity_fingerprint(CopyFingerprint(identity_fingerprint)) {}
|
||||
|
||||
TransportDescription::TransportDescription(absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd)
|
||||
: ice_ufrag(ice_ufrag),
|
||||
ice_pwd(ice_pwd),
|
||||
ice_mode(ICEMODE_FULL),
|
||||
connection_role(CONNECTIONROLE_NONE) {}
|
||||
|
||||
TransportDescription::TransportDescription(const TransportDescription& from)
|
||||
: transport_options(from.transport_options),
|
||||
ice_ufrag(from.ice_ufrag),
|
||||
ice_pwd(from.ice_pwd),
|
||||
ice_mode(from.ice_mode),
|
||||
connection_role(from.connection_role),
|
||||
identity_fingerprint(CopyFingerprint(from.identity_fingerprint.get())) {}
|
||||
|
||||
TransportDescription::~TransportDescription() = default;
|
||||
|
||||
TransportDescription& TransportDescription::operator=(
|
||||
const TransportDescription& from) {
|
||||
// Self-assignment
|
||||
if (this == &from)
|
||||
return *this;
|
||||
|
||||
transport_options = from.transport_options;
|
||||
ice_ufrag = from.ice_ufrag;
|
||||
ice_pwd = from.ice_pwd;
|
||||
ice_mode = from.ice_mode;
|
||||
connection_role = from.connection_role;
|
||||
|
||||
identity_fingerprint.reset(CopyFingerprint(from.identity_fingerprint.get()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
143
TMessagesProj/jni/voip/webrtc/p2p/base/transport_description.h
Normal file
143
TMessagesProj/jni/voip/webrtc/p2p/base/transport_description.h
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright 2012 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_TRANSPORT_DESCRIPTION_H_
|
||||
#define P2P_BASE_TRANSPORT_DESCRIPTION_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/rtc_error.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "rtc_base/ssl_fingerprint.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// Whether our side of the call is driving the negotiation, or the other side.
|
||||
enum IceRole { ICEROLE_CONTROLLING = 0, ICEROLE_CONTROLLED, ICEROLE_UNKNOWN };
|
||||
|
||||
// ICE RFC 5245 implementation type.
|
||||
enum IceMode {
|
||||
ICEMODE_FULL, // As defined in http://tools.ietf.org/html/rfc5245#section-4.1
|
||||
ICEMODE_LITE // As defined in http://tools.ietf.org/html/rfc5245#section-4.2
|
||||
};
|
||||
|
||||
// RFC 4145 - http://tools.ietf.org/html/rfc4145#section-4
|
||||
// 'active': The endpoint will initiate an outgoing connection.
|
||||
// 'passive': The endpoint will accept an incoming connection.
|
||||
// 'actpass': The endpoint is willing to accept an incoming
|
||||
// connection or to initiate an outgoing connection.
|
||||
enum ConnectionRole {
|
||||
CONNECTIONROLE_NONE = 0,
|
||||
CONNECTIONROLE_ACTIVE,
|
||||
CONNECTIONROLE_PASSIVE,
|
||||
CONNECTIONROLE_ACTPASS,
|
||||
CONNECTIONROLE_HOLDCONN,
|
||||
};
|
||||
|
||||
struct IceParameters {
|
||||
// Constructs an IceParameters from a user-provided ufrag/pwd combination.
|
||||
// Returns a SyntaxError if the ufrag or pwd are malformed.
|
||||
static RTC_EXPORT webrtc::RTCErrorOr<IceParameters> Parse(
|
||||
absl::string_view raw_ufrag,
|
||||
absl::string_view raw_pwd);
|
||||
|
||||
// TODO(honghaiz): Include ICE mode in this structure to match the ORTC
|
||||
// struct:
|
||||
// http://ortc.org/wp-content/uploads/2016/03/ortc.html#idl-def-RTCIceParameters
|
||||
std::string ufrag;
|
||||
std::string pwd;
|
||||
bool renomination = false;
|
||||
IceParameters() = default;
|
||||
IceParameters(absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd,
|
||||
bool ice_renomination)
|
||||
: ufrag(ice_ufrag), pwd(ice_pwd), renomination(ice_renomination) {}
|
||||
|
||||
bool operator==(const IceParameters& other) const {
|
||||
return ufrag == other.ufrag && pwd == other.pwd &&
|
||||
renomination == other.renomination;
|
||||
}
|
||||
bool operator!=(const IceParameters& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
// Validate IceParameters, returns a SyntaxError if the ufrag or pwd are
|
||||
// malformed.
|
||||
webrtc::RTCError Validate() const;
|
||||
};
|
||||
|
||||
extern const char CONNECTIONROLE_ACTIVE_STR[];
|
||||
extern const char CONNECTIONROLE_PASSIVE_STR[];
|
||||
extern const char CONNECTIONROLE_ACTPASS_STR[];
|
||||
extern const char CONNECTIONROLE_HOLDCONN_STR[];
|
||||
|
||||
constexpr auto* ICE_OPTION_TRICKLE = "trickle";
|
||||
constexpr auto* ICE_OPTION_RENOMINATION = "renomination";
|
||||
|
||||
absl::optional<ConnectionRole> StringToConnectionRole(
|
||||
absl::string_view role_str);
|
||||
bool ConnectionRoleToString(const ConnectionRole& role, std::string* role_str);
|
||||
|
||||
struct TransportDescription {
|
||||
TransportDescription();
|
||||
TransportDescription(const std::vector<std::string>& transport_options,
|
||||
absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd,
|
||||
IceMode ice_mode,
|
||||
ConnectionRole role,
|
||||
const rtc::SSLFingerprint* identity_fingerprint);
|
||||
TransportDescription(absl::string_view ice_ufrag, absl::string_view ice_pwd);
|
||||
TransportDescription(const TransportDescription& from);
|
||||
~TransportDescription();
|
||||
|
||||
TransportDescription& operator=(const TransportDescription& from);
|
||||
|
||||
// TODO(deadbeef): Rename to HasIceOption, etc.
|
||||
bool HasOption(absl::string_view option) const {
|
||||
return absl::c_linear_search(transport_options, option);
|
||||
}
|
||||
void AddOption(absl::string_view option) {
|
||||
transport_options.emplace_back(option);
|
||||
}
|
||||
bool secure() const { return identity_fingerprint != nullptr; }
|
||||
|
||||
IceParameters GetIceParameters() const {
|
||||
return IceParameters(ice_ufrag, ice_pwd,
|
||||
HasOption(ICE_OPTION_RENOMINATION));
|
||||
}
|
||||
|
||||
static rtc::SSLFingerprint* CopyFingerprint(const rtc::SSLFingerprint* from) {
|
||||
if (!from)
|
||||
return NULL;
|
||||
|
||||
return new rtc::SSLFingerprint(*from);
|
||||
}
|
||||
|
||||
// These are actually ICE options (appearing in the ice-options attribute in
|
||||
// SDP).
|
||||
// TODO(deadbeef): Rename to ice_options.
|
||||
std::vector<std::string> transport_options;
|
||||
std::string ice_ufrag;
|
||||
std::string ice_pwd;
|
||||
IceMode ice_mode;
|
||||
ConnectionRole connection_role;
|
||||
|
||||
std::unique_ptr<rtc::SSLFingerprint> identity_fingerprint;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_TRANSPORT_DESCRIPTION_H_
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright 2012 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/transport_description_factory.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/ssl_fingerprint.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
TransportDescriptionFactory::TransportDescriptionFactory(
|
||||
const webrtc::FieldTrialsView& field_trials)
|
||||
: field_trials_(field_trials) {}
|
||||
|
||||
TransportDescriptionFactory::~TransportDescriptionFactory() = default;
|
||||
|
||||
std::unique_ptr<TransportDescription> TransportDescriptionFactory::CreateOffer(
|
||||
const TransportOptions& options,
|
||||
const TransportDescription* current_description,
|
||||
IceCredentialsIterator* ice_credentials) const {
|
||||
auto desc = std::make_unique<TransportDescription>();
|
||||
|
||||
// Generate the ICE credentials if we don't already have them.
|
||||
if (!current_description || options.ice_restart) {
|
||||
IceParameters credentials = ice_credentials->GetIceCredentials();
|
||||
desc->ice_ufrag = credentials.ufrag;
|
||||
desc->ice_pwd = credentials.pwd;
|
||||
} else {
|
||||
desc->ice_ufrag = current_description->ice_ufrag;
|
||||
desc->ice_pwd = current_description->ice_pwd;
|
||||
}
|
||||
desc->AddOption(ICE_OPTION_TRICKLE);
|
||||
if (options.enable_ice_renomination) {
|
||||
desc->AddOption(ICE_OPTION_RENOMINATION);
|
||||
}
|
||||
|
||||
// If we are not trying to establish a secure transport, don't add a
|
||||
// fingerprint.
|
||||
if (insecure_ && !certificate_) {
|
||||
return desc;
|
||||
}
|
||||
// Fail if we can't create the fingerprint.
|
||||
// If we are the initiator set role to "actpass".
|
||||
if (!SetSecurityInfo(desc.get(), CONNECTIONROLE_ACTPASS)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
std::unique_ptr<TransportDescription> TransportDescriptionFactory::CreateAnswer(
|
||||
const TransportDescription* offer,
|
||||
const TransportOptions& options,
|
||||
bool require_transport_attributes,
|
||||
const TransportDescription* current_description,
|
||||
IceCredentialsIterator* ice_credentials) const {
|
||||
// TODO(juberti): Figure out why we get NULL offers, and fix this upstream.
|
||||
if (!offer) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to create TransportDescription answer "
|
||||
"because offer is NULL";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auto desc = std::make_unique<TransportDescription>();
|
||||
// Generate the ICE credentials if we don't already have them or ice is
|
||||
// being restarted.
|
||||
if (!current_description || options.ice_restart) {
|
||||
IceParameters credentials = ice_credentials->GetIceCredentials();
|
||||
desc->ice_ufrag = credentials.ufrag;
|
||||
desc->ice_pwd = credentials.pwd;
|
||||
} else {
|
||||
desc->ice_ufrag = current_description->ice_ufrag;
|
||||
desc->ice_pwd = current_description->ice_pwd;
|
||||
}
|
||||
desc->AddOption(ICE_OPTION_TRICKLE);
|
||||
if (options.enable_ice_renomination) {
|
||||
desc->AddOption(ICE_OPTION_RENOMINATION);
|
||||
}
|
||||
// Special affordance for testing: Answer without DTLS params
|
||||
// if we are insecure without a certificate, or if we are
|
||||
// insecure with a non-DTLS offer.
|
||||
if ((!certificate_ || !offer->identity_fingerprint.get()) && insecure()) {
|
||||
return desc;
|
||||
}
|
||||
if (!offer->identity_fingerprint.get()) {
|
||||
if (require_transport_attributes) {
|
||||
// We require DTLS, but the other side didn't offer it. Fail.
|
||||
RTC_LOG(LS_WARNING) << "Failed to create TransportDescription answer "
|
||||
"because of incompatible security settings";
|
||||
return NULL;
|
||||
}
|
||||
// This may be a bundled section, fingerprint may legitimately be missing.
|
||||
return desc;
|
||||
}
|
||||
// Negotiate security params.
|
||||
// The offer supports DTLS, so answer with DTLS.
|
||||
RTC_CHECK(certificate_);
|
||||
ConnectionRole role = CONNECTIONROLE_NONE;
|
||||
// If the offer does not constrain the role, go with preference.
|
||||
if (offer->connection_role == CONNECTIONROLE_ACTPASS) {
|
||||
role = (options.prefer_passive_role) ? CONNECTIONROLE_PASSIVE
|
||||
: CONNECTIONROLE_ACTIVE;
|
||||
} else if (offer->connection_role == CONNECTIONROLE_ACTIVE) {
|
||||
role = CONNECTIONROLE_PASSIVE;
|
||||
} else if (offer->connection_role == CONNECTIONROLE_PASSIVE) {
|
||||
role = CONNECTIONROLE_ACTIVE;
|
||||
} else if (offer->connection_role == CONNECTIONROLE_NONE) {
|
||||
// This case may be reached if a=setup is not present in the SDP.
|
||||
RTC_LOG(LS_WARNING) << "Remote offer connection role is NONE, which is "
|
||||
"a protocol violation";
|
||||
role = (options.prefer_passive_role) ? CONNECTIONROLE_PASSIVE
|
||||
: CONNECTIONROLE_ACTIVE;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Remote offer connection role is " << role
|
||||
<< " which is a protocol violation";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return NULL;
|
||||
}
|
||||
if (!SetSecurityInfo(desc.get(), role)) {
|
||||
return NULL;
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
bool TransportDescriptionFactory::SetSecurityInfo(TransportDescription* desc,
|
||||
ConnectionRole role) const {
|
||||
if (!certificate_) {
|
||||
RTC_LOG(LS_ERROR) << "Cannot create identity digest with no certificate";
|
||||
return false;
|
||||
}
|
||||
|
||||
// This digest algorithm is used to produce the a=fingerprint lines in SDP.
|
||||
// RFC 4572 Section 5 requires that those lines use the same hash function as
|
||||
// the certificate's signature, which is what CreateFromCertificate does.
|
||||
desc->identity_fingerprint =
|
||||
rtc::SSLFingerprint::CreateFromCertificate(*certificate_);
|
||||
if (!desc->identity_fingerprint) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assign security role.
|
||||
desc->connection_role = role;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright 2012 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_TRANSPORT_DESCRIPTION_FACTORY_H_
|
||||
#define P2P_BASE_TRANSPORT_DESCRIPTION_FACTORY_H_
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "api/field_trials_view.h"
|
||||
#include "p2p/base/ice_credentials_iterator.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "rtc_base/rtc_certificate.h"
|
||||
|
||||
namespace rtc {
|
||||
class SSLIdentity;
|
||||
}
|
||||
|
||||
namespace cricket {
|
||||
|
||||
struct TransportOptions {
|
||||
bool ice_restart = false;
|
||||
bool prefer_passive_role = false;
|
||||
// If true, ICE renomination is supported and will be used if it is also
|
||||
// supported by the remote side.
|
||||
bool enable_ice_renomination = false;
|
||||
};
|
||||
|
||||
// Creates transport descriptions according to the supplied configuration.
|
||||
// When creating answers, performs the appropriate negotiation
|
||||
// of the various fields to determine the proper result.
|
||||
class TransportDescriptionFactory {
|
||||
public:
|
||||
// Default ctor; use methods below to set configuration.
|
||||
explicit TransportDescriptionFactory(
|
||||
const webrtc::FieldTrialsView& field_trials);
|
||||
~TransportDescriptionFactory();
|
||||
|
||||
// The certificate to use when setting up DTLS.
|
||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate() const {
|
||||
return certificate_;
|
||||
}
|
||||
|
||||
// Specifies the certificate to use
|
||||
void set_certificate(rtc::scoped_refptr<rtc::RTCCertificate> certificate) {
|
||||
certificate_ = std::move(certificate);
|
||||
}
|
||||
|
||||
// Creates a transport description suitable for use in an offer.
|
||||
std::unique_ptr<TransportDescription> CreateOffer(
|
||||
const TransportOptions& options,
|
||||
const TransportDescription* current_description,
|
||||
IceCredentialsIterator* ice_credentials) const;
|
||||
// Create a transport description that is a response to an offer.
|
||||
//
|
||||
// If `require_transport_attributes` is true, then TRANSPORT category
|
||||
// attributes are expected to be present in `offer`, as defined by
|
||||
// sdp-mux-attributes, and null will be returned otherwise. It's expected
|
||||
// that this will be set to false for an m= section that's in a BUNDLE group
|
||||
// but isn't the first m= section in the group.
|
||||
std::unique_ptr<TransportDescription> CreateAnswer(
|
||||
const TransportDescription* offer,
|
||||
const TransportOptions& options,
|
||||
bool require_transport_attributes,
|
||||
const TransportDescription* current_description,
|
||||
IceCredentialsIterator* ice_credentials) const;
|
||||
|
||||
const webrtc::FieldTrialsView& trials() const { return field_trials_; }
|
||||
// Functions for disabling encryption - test only!
|
||||
// In insecure mode, the connection will accept a description without
|
||||
// fingerprint, and will generate SDP even if certificate is not set.
|
||||
// If certificate is set, it will accept a description both with and
|
||||
// without fingerprint, but will generate a description with fingerprint.
|
||||
bool insecure() const { return insecure_; }
|
||||
void SetInsecureForTesting() { insecure_ = true; }
|
||||
|
||||
private:
|
||||
bool SetSecurityInfo(TransportDescription* description,
|
||||
ConnectionRole role) const;
|
||||
bool insecure_ = false;
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
|
||||
const webrtc::FieldTrialsView& field_trials_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_TRANSPORT_DESCRIPTION_FACTORY_H_
|
||||
42
TMessagesProj/jni/voip/webrtc/p2p/base/transport_info.h
Normal file
42
TMessagesProj/jni/voip/webrtc/p2p/base/transport_info.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2012 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_TRANSPORT_INFO_H_
|
||||
#define P2P_BASE_TRANSPORT_INFO_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/candidate.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "rtc_base/helpers.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// A TransportInfo is NOT a transport-info message. It is comparable
|
||||
// to a "ContentInfo". A transport-infos message is basically just a
|
||||
// collection of TransportInfos.
|
||||
struct TransportInfo {
|
||||
TransportInfo() {}
|
||||
|
||||
TransportInfo(const std::string& content_name,
|
||||
const TransportDescription& description)
|
||||
: content_name(content_name), description(description) {}
|
||||
|
||||
std::string content_name;
|
||||
TransportDescription description;
|
||||
};
|
||||
|
||||
typedef std::vector<TransportInfo> TransportInfos;
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_TRANSPORT_INFO_H_
|
||||
1880
TMessagesProj/jni/voip/webrtc/p2p/base/turn_port.cc
Normal file
1880
TMessagesProj/jni/voip/webrtc/p2p/base/turn_port.cc
Normal file
File diff suppressed because it is too large
Load diff
374
TMessagesProj/jni/voip/webrtc/p2p/base/turn_port.h
Normal file
374
TMessagesProj/jni/voip/webrtc/p2p/base/turn_port.h
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
/*
|
||||
* Copyright 2012 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_TURN_PORT_H_
|
||||
#define P2P_BASE_TURN_PORT_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/async_dns_resolver.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "p2p/client/relay_port_factory_interface.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/network/received_packet.h"
|
||||
#include "rtc_base/ssl_certificate.h"
|
||||
|
||||
namespace webrtc {
|
||||
class TurnCustomizer;
|
||||
}
|
||||
|
||||
namespace cricket {
|
||||
|
||||
const int kMaxTurnUsernameLength = 509; // RFC 8489 section 14.3
|
||||
|
||||
extern const int STUN_ATTR_TURN_LOGGING_ID;
|
||||
extern const char TURN_PORT_TYPE[];
|
||||
class TurnAllocateRequest;
|
||||
class TurnEntry;
|
||||
|
||||
class TurnPort : public Port {
|
||||
public:
|
||||
enum PortState {
|
||||
STATE_CONNECTING, // Initial state, cannot send any packets.
|
||||
STATE_CONNECTED, // Socket connected, ready to send stun requests.
|
||||
STATE_READY, // Received allocate success, can send any packets.
|
||||
STATE_RECEIVEONLY, // Had REFRESH_REQUEST error, cannot send any packets.
|
||||
STATE_DISCONNECTED, // TCP connection died, cannot send/receive any
|
||||
// packets.
|
||||
};
|
||||
|
||||
static bool Validate(const CreateRelayPortArgs& args) {
|
||||
// Do basic parameter validation.
|
||||
if (args.config->credentials.username.size() > kMaxTurnUsernameLength) {
|
||||
RTC_LOG(LS_ERROR) << "Attempt to use TURN with a too long username "
|
||||
<< "of length "
|
||||
<< args.config->credentials.username.size();
|
||||
return false;
|
||||
}
|
||||
// Do not connect to low-numbered ports. The default STUN port is 3478.
|
||||
if (!AllowedTurnPort(args.server_address->address.port(),
|
||||
args.field_trials)) {
|
||||
RTC_LOG(LS_ERROR) << "Attempt to use TURN to connect to port "
|
||||
<< args.server_address->address.port();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create a TURN port using the shared UDP socket, `socket`.
|
||||
static std::unique_ptr<TurnPort> Create(const CreateRelayPortArgs& args,
|
||||
rtc::AsyncPacketSocket* socket) {
|
||||
if (!Validate(args)) {
|
||||
return nullptr;
|
||||
}
|
||||
// Using `new` to access a non-public constructor.
|
||||
return absl::WrapUnique(
|
||||
new TurnPort(args.network_thread, args.socket_factory, args.network,
|
||||
socket, args.username, args.password, *args.server_address,
|
||||
args.config->credentials, args.relative_priority,
|
||||
args.config->tls_alpn_protocols,
|
||||
args.config->tls_elliptic_curves, args.turn_customizer,
|
||||
args.config->tls_cert_verifier, args.field_trials));
|
||||
}
|
||||
|
||||
// Create a TURN port that will use a new socket, bound to `network` and
|
||||
// using a port in the range between `min_port` and `max_port`.
|
||||
static std::unique_ptr<TurnPort> Create(const CreateRelayPortArgs& args,
|
||||
int min_port,
|
||||
int max_port) {
|
||||
if (!Validate(args)) {
|
||||
return nullptr;
|
||||
}
|
||||
// Using `new` to access a non-public constructor.
|
||||
return absl::WrapUnique(
|
||||
new TurnPort(args.network_thread, args.socket_factory, args.network,
|
||||
min_port, max_port, args.username, args.password,
|
||||
*args.server_address, args.config->credentials,
|
||||
args.relative_priority, args.config->tls_alpn_protocols,
|
||||
args.config->tls_elliptic_curves, args.turn_customizer,
|
||||
args.config->tls_cert_verifier, args.field_trials));
|
||||
}
|
||||
|
||||
~TurnPort() override;
|
||||
|
||||
const ProtocolAddress& server_address() const { return server_address_; }
|
||||
// Returns an empty address if the local address has not been assigned.
|
||||
rtc::SocketAddress GetLocalAddress() const;
|
||||
|
||||
bool ready() const { return state_ == STATE_READY; }
|
||||
bool connected() const {
|
||||
return state_ == STATE_READY || state_ == STATE_CONNECTED;
|
||||
}
|
||||
const RelayCredentials& credentials() const { return credentials_; }
|
||||
|
||||
ProtocolType GetProtocol() const override;
|
||||
|
||||
virtual TlsCertPolicy GetTlsCertPolicy() const;
|
||||
virtual void SetTlsCertPolicy(TlsCertPolicy tls_cert_policy);
|
||||
|
||||
void SetTurnLoggingId(absl::string_view turn_logging_id);
|
||||
|
||||
virtual std::vector<std::string> GetTlsAlpnProtocols() const;
|
||||
virtual std::vector<std::string> GetTlsEllipticCurves() const;
|
||||
|
||||
// Release a TURN allocation by sending a refresh with lifetime 0.
|
||||
// Sets state to STATE_RECEIVEONLY.
|
||||
void Release();
|
||||
|
||||
void PrepareAddress() override;
|
||||
Connection* CreateConnection(const Candidate& c,
|
||||
PortInterface::CandidateOrigin origin) override;
|
||||
int SendTo(const void* data,
|
||||
size_t size,
|
||||
const rtc::SocketAddress& addr,
|
||||
const rtc::PacketOptions& options,
|
||||
bool payload) override;
|
||||
int SetOption(rtc::Socket::Option opt, int value) override;
|
||||
int GetOption(rtc::Socket::Option opt, int* value) override;
|
||||
int GetError() override;
|
||||
|
||||
bool HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet) override;
|
||||
bool CanHandleIncomingPacketsFrom(
|
||||
const rtc::SocketAddress& addr) const override;
|
||||
|
||||
// Checks if a connection exists for `addr` before forwarding the call to
|
||||
// the base class.
|
||||
void SendBindingErrorResponse(StunMessage* message,
|
||||
const rtc::SocketAddress& addr,
|
||||
int error_code,
|
||||
absl::string_view reason) override;
|
||||
|
||||
virtual void OnReadPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet);
|
||||
|
||||
void OnSentPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::SentPacket& sent_packet) override;
|
||||
virtual void OnReadyToSend(rtc::AsyncPacketSocket* socket);
|
||||
bool SupportsProtocol(absl::string_view protocol) const override;
|
||||
|
||||
void OnSocketConnect(rtc::AsyncPacketSocket* socket);
|
||||
void OnSocketClose(rtc::AsyncPacketSocket* socket, int error);
|
||||
|
||||
const std::string& hash() const { return hash_; }
|
||||
const std::string& nonce() const { return nonce_; }
|
||||
|
||||
int error() const { return error_; }
|
||||
|
||||
void OnAllocateMismatch();
|
||||
|
||||
rtc::AsyncPacketSocket* socket() const { return socket_; }
|
||||
StunRequestManager& request_manager() { return request_manager_; }
|
||||
|
||||
bool HasRequests() { return !request_manager_.empty(); }
|
||||
void set_credentials(const RelayCredentials& credentials) {
|
||||
credentials_ = credentials;
|
||||
}
|
||||
// Finds the turn entry with `address` and sets its channel id.
|
||||
// Returns true if the entry is found.
|
||||
bool SetEntryChannelId(const rtc::SocketAddress& address, int channel_id);
|
||||
|
||||
void HandleConnectionDestroyed(Connection* conn) override;
|
||||
|
||||
void CloseForTest() { Close(); }
|
||||
|
||||
// TODO(solenberg): Tests should be refactored to not peek at internal state.
|
||||
class CallbacksForTest {
|
||||
public:
|
||||
virtual ~CallbacksForTest() {}
|
||||
virtual void OnTurnCreatePermissionResult(int code) = 0;
|
||||
virtual void OnTurnRefreshResult(int code) = 0;
|
||||
virtual void OnTurnPortClosed() = 0;
|
||||
};
|
||||
void SetCallbacksForTest(CallbacksForTest* callbacks);
|
||||
|
||||
protected:
|
||||
TurnPort(webrtc::TaskQueueBase* thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
rtc::AsyncPacketSocket* socket,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
const ProtocolAddress& server_address,
|
||||
const RelayCredentials& credentials,
|
||||
int server_priority,
|
||||
const std::vector<std::string>& tls_alpn_protocols,
|
||||
const std::vector<std::string>& tls_elliptic_curves,
|
||||
webrtc::TurnCustomizer* customizer,
|
||||
rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr,
|
||||
const webrtc::FieldTrialsView* field_trials = nullptr);
|
||||
|
||||
TurnPort(webrtc::TaskQueueBase* thread,
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::Network* network,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
const ProtocolAddress& server_address,
|
||||
const RelayCredentials& credentials,
|
||||
int server_priority,
|
||||
const std::vector<std::string>& tls_alpn_protocols,
|
||||
const std::vector<std::string>& tls_elliptic_curves,
|
||||
webrtc::TurnCustomizer* customizer,
|
||||
rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr,
|
||||
const webrtc::FieldTrialsView* field_trials = nullptr);
|
||||
|
||||
// NOTE: This method needs to be accessible for StunPort
|
||||
// return true if entry was created (i.e channel_number consumed).
|
||||
bool CreateOrRefreshEntry(Connection* conn, int channel_number);
|
||||
|
||||
rtc::DiffServCodePoint StunDscpValue() const override;
|
||||
|
||||
// Shuts down the turn port, frees requests and deletes connections.
|
||||
void Close();
|
||||
|
||||
private:
|
||||
typedef std::map<rtc::Socket::Option, int> SocketOptionsMap;
|
||||
typedef std::set<rtc::SocketAddress> AttemptedServerSet;
|
||||
|
||||
static bool AllowedTurnPort(int port,
|
||||
const webrtc::FieldTrialsView* field_trials);
|
||||
void TryAlternateServer();
|
||||
|
||||
bool CreateTurnClientSocket();
|
||||
|
||||
void set_nonce(absl::string_view nonce) { nonce_ = std::string(nonce); }
|
||||
void set_realm(absl::string_view realm) {
|
||||
if (realm != realm_) {
|
||||
realm_ = std::string(realm);
|
||||
UpdateHash();
|
||||
}
|
||||
}
|
||||
|
||||
void OnRefreshError();
|
||||
void HandleRefreshError();
|
||||
bool SetAlternateServer(const rtc::SocketAddress& address);
|
||||
void ResolveTurnAddress(const rtc::SocketAddress& address);
|
||||
void OnResolveResult(const webrtc::AsyncDnsResolverResult& result);
|
||||
|
||||
void AddRequestAuthInfo(StunMessage* msg);
|
||||
void OnSendStunPacket(const void* data, size_t size, StunRequest* request);
|
||||
// Stun address from allocate success response.
|
||||
// Currently used only for testing.
|
||||
void OnStunAddress(const rtc::SocketAddress& address);
|
||||
void OnAllocateSuccess(const rtc::SocketAddress& address,
|
||||
const rtc::SocketAddress& stun_address);
|
||||
void OnAllocateError(int error_code, absl::string_view reason);
|
||||
void OnAllocateRequestTimeout();
|
||||
|
||||
void HandleDataIndication(const char* data,
|
||||
size_t size,
|
||||
int64_t packet_time_us);
|
||||
void HandleChannelData(int channel_id,
|
||||
const char* data,
|
||||
size_t size,
|
||||
int64_t packet_time_us);
|
||||
void DispatchPacket(const char* data,
|
||||
size_t size,
|
||||
const rtc::SocketAddress& remote_addr,
|
||||
ProtocolType proto,
|
||||
int64_t packet_time_us);
|
||||
|
||||
bool ScheduleRefresh(uint32_t lifetime);
|
||||
void SendRequest(StunRequest* request, int delay);
|
||||
int Send(const void* data, size_t size, const rtc::PacketOptions& options);
|
||||
void UpdateHash();
|
||||
bool UpdateNonce(StunMessage* response);
|
||||
void ResetNonce();
|
||||
|
||||
bool HasPermission(const rtc::IPAddress& ipaddr) const;
|
||||
TurnEntry* FindEntry(const rtc::SocketAddress& address) const;
|
||||
TurnEntry* FindEntry(int channel_id) const;
|
||||
|
||||
// Marks the connection with remote address `address` failed and
|
||||
// pruned (a.k.a. write-timed-out). Returns true if a connection is found.
|
||||
bool FailAndPruneConnection(const rtc::SocketAddress& address);
|
||||
|
||||
void MaybeAddTurnLoggingId(StunMessage* message);
|
||||
|
||||
void TurnCustomizerMaybeModifyOutgoingStunMessage(StunMessage* message);
|
||||
bool TurnCustomizerAllowChannelData(const void* data,
|
||||
size_t size,
|
||||
bool payload);
|
||||
|
||||
ProtocolAddress server_address_;
|
||||
// Reconstruct the URL of the server which the candidate is gathered from.
|
||||
// A copy needs to be stored as server_address_ will resolve and clear its
|
||||
// hostname field.
|
||||
std::string ReconstructServerUrl();
|
||||
std::string server_url_;
|
||||
|
||||
TlsCertPolicy tls_cert_policy_ = TlsCertPolicy::TLS_CERT_POLICY_SECURE;
|
||||
std::vector<std::string> tls_alpn_protocols_;
|
||||
std::vector<std::string> tls_elliptic_curves_;
|
||||
rtc::SSLCertificateVerifier* tls_cert_verifier_;
|
||||
RelayCredentials credentials_;
|
||||
AttemptedServerSet attempted_server_addresses_;
|
||||
|
||||
rtc::AsyncPacketSocket* socket_;
|
||||
SocketOptionsMap socket_options_;
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface> resolver_;
|
||||
int error_;
|
||||
rtc::DiffServCodePoint stun_dscp_value_;
|
||||
|
||||
StunRequestManager request_manager_;
|
||||
std::string realm_; // From 401/438 response message.
|
||||
std::string nonce_; // From 401/438 response message.
|
||||
std::string hash_; // Digest of username:realm:password
|
||||
|
||||
int next_channel_number_;
|
||||
std::vector<std::unique_ptr<TurnEntry>> entries_;
|
||||
|
||||
PortState state_;
|
||||
// By default the value will be set to 0. This value will be used in
|
||||
// calculating the candidate priority.
|
||||
int server_priority_;
|
||||
|
||||
// The number of retries made due to allocate mismatch error.
|
||||
size_t allocate_mismatch_retries_;
|
||||
|
||||
// Optional TurnCustomizer that can modify outgoing messages. Once set, this
|
||||
// must outlive the TurnPort's lifetime.
|
||||
webrtc::TurnCustomizer* turn_customizer_ = nullptr;
|
||||
|
||||
// Optional TurnLoggingId.
|
||||
// An identifier set by application that is added to TURN_ALLOCATE_REQUEST
|
||||
// and can be used to match client/backend logs.
|
||||
// TODO(jonaso): This should really be initialized in constructor,
|
||||
// but that is currently so terrible. Fix once constructor is changed
|
||||
// to be more easy to work with.
|
||||
std::string turn_logging_id_;
|
||||
|
||||
webrtc::ScopedTaskSafety task_safety_;
|
||||
|
||||
CallbacksForTest* callbacks_for_test_ = nullptr;
|
||||
|
||||
friend class TurnEntry;
|
||||
friend class TurnAllocateRequest;
|
||||
friend class TurnRefreshRequest;
|
||||
friend class TurnCreatePermissionRequest;
|
||||
friend class TurnChannelBindRequest;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_TURN_PORT_H_
|
||||
879
TMessagesProj/jni/voip/webrtc/p2p/base/turn_server.cc
Normal file
879
TMessagesProj/jni/voip/webrtc/p2p/base/turn_server.cc
Normal file
|
|
@ -0,0 +1,879 @@
|
|||
/*
|
||||
* Copyright 2012 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/turn_server.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <tuple> // for std::tie
|
||||
#include <utility>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/array_view.h"
|
||||
#include "api/packet_socket_factory.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "api/transport/stun.h"
|
||||
#include "p2p/base/async_stun_tcp_socket.h"
|
||||
#include "rtc_base/byte_buffer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/helpers.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/message_digest.h"
|
||||
#include "rtc_base/socket_adapters.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
|
||||
namespace cricket {
|
||||
namespace {
|
||||
using ::webrtc::TimeDelta;
|
||||
|
||||
// TODO(juberti): Move this all to a future turnmessage.h
|
||||
// static const int IPPROTO_UDP = 17;
|
||||
constexpr TimeDelta kNonceTimeout = TimeDelta::Minutes(60);
|
||||
constexpr TimeDelta kDefaultAllocationTimeout = TimeDelta::Minutes(10);
|
||||
constexpr TimeDelta kPermissionTimeout = TimeDelta::Minutes(5);
|
||||
constexpr TimeDelta kChannelTimeout = TimeDelta::Minutes(10);
|
||||
|
||||
constexpr int kMinChannelNumber = 0x4000;
|
||||
constexpr int kMaxChannelNumber = 0x7FFF;
|
||||
|
||||
constexpr size_t kNonceKeySize = 16;
|
||||
constexpr size_t kNonceSize = 48;
|
||||
|
||||
constexpr size_t TURN_CHANNEL_HEADER_SIZE = 4U;
|
||||
|
||||
// TODO(mallinath) - Move these to a common place.
|
||||
bool IsTurnChannelData(uint16_t msg_type) {
|
||||
// The first two bits of a channel data message are 0b01.
|
||||
return ((msg_type & 0xC000) == 0x4000);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int GetStunSuccessResponseTypeOrZero(const StunMessage& req) {
|
||||
const int resp_type = GetStunSuccessResponseType(req.type());
|
||||
return resp_type == -1 ? 0 : resp_type;
|
||||
}
|
||||
|
||||
int GetStunErrorResponseTypeOrZero(const StunMessage& req) {
|
||||
const int resp_type = GetStunErrorResponseType(req.type());
|
||||
return resp_type == -1 ? 0 : resp_type;
|
||||
}
|
||||
|
||||
static void InitErrorResponse(int code,
|
||||
absl::string_view reason,
|
||||
StunMessage* resp) {
|
||||
resp->AddAttribute(std::make_unique<cricket::StunErrorCodeAttribute>(
|
||||
STUN_ATTR_ERROR_CODE, code, std::string(reason)));
|
||||
}
|
||||
|
||||
TurnServer::TurnServer(webrtc::TaskQueueBase* thread)
|
||||
: thread_(thread),
|
||||
nonce_key_(rtc::CreateRandomString(kNonceKeySize)),
|
||||
auth_hook_(NULL),
|
||||
redirect_hook_(NULL),
|
||||
enable_otu_nonce_(false) {}
|
||||
|
||||
TurnServer::~TurnServer() {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
for (InternalSocketMap::iterator it = server_sockets_.begin();
|
||||
it != server_sockets_.end(); ++it) {
|
||||
rtc::AsyncPacketSocket* socket = it->first;
|
||||
delete socket;
|
||||
}
|
||||
|
||||
for (ServerSocketMap::iterator it = server_listen_sockets_.begin();
|
||||
it != server_listen_sockets_.end(); ++it) {
|
||||
rtc::Socket* socket = it->first;
|
||||
delete socket;
|
||||
}
|
||||
}
|
||||
|
||||
void TurnServer::AddInternalSocket(rtc::AsyncPacketSocket* socket,
|
||||
ProtocolType proto) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
RTC_DCHECK(server_sockets_.end() == server_sockets_.find(socket));
|
||||
server_sockets_[socket] = proto;
|
||||
socket->RegisterReceivedPacketCallback(
|
||||
[&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
OnInternalPacket(socket, packet);
|
||||
});
|
||||
}
|
||||
|
||||
void TurnServer::AddInternalServerSocket(
|
||||
rtc::Socket* socket,
|
||||
ProtocolType proto,
|
||||
std::unique_ptr<rtc::SSLAdapterFactory> ssl_adapter_factory) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
|
||||
RTC_DCHECK(server_listen_sockets_.end() ==
|
||||
server_listen_sockets_.find(socket));
|
||||
server_listen_sockets_[socket] = {proto, std::move(ssl_adapter_factory)};
|
||||
socket->SignalReadEvent.connect(this, &TurnServer::OnNewInternalConnection);
|
||||
}
|
||||
|
||||
void TurnServer::SetExternalSocketFactory(
|
||||
rtc::PacketSocketFactory* factory,
|
||||
const rtc::SocketAddress& external_addr) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
external_socket_factory_.reset(factory);
|
||||
external_addr_ = external_addr;
|
||||
}
|
||||
|
||||
void TurnServer::OnNewInternalConnection(rtc::Socket* socket) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
RTC_DCHECK(server_listen_sockets_.find(socket) !=
|
||||
server_listen_sockets_.end());
|
||||
AcceptConnection(socket);
|
||||
}
|
||||
|
||||
void TurnServer::AcceptConnection(rtc::Socket* server_socket) {
|
||||
// Check if someone is trying to connect to us.
|
||||
rtc::SocketAddress accept_addr;
|
||||
rtc::Socket* accepted_socket = server_socket->Accept(&accept_addr);
|
||||
if (accepted_socket != NULL) {
|
||||
const ServerSocketInfo& info = server_listen_sockets_[server_socket];
|
||||
if (info.ssl_adapter_factory) {
|
||||
rtc::SSLAdapter* ssl_adapter =
|
||||
info.ssl_adapter_factory->CreateAdapter(accepted_socket);
|
||||
ssl_adapter->StartSSL("");
|
||||
accepted_socket = ssl_adapter;
|
||||
}
|
||||
cricket::AsyncStunTCPSocket* tcp_socket =
|
||||
new cricket::AsyncStunTCPSocket(accepted_socket);
|
||||
|
||||
tcp_socket->SubscribeCloseEvent(this,
|
||||
[this](rtc::AsyncPacketSocket* s, int err) {
|
||||
OnInternalSocketClose(s, err);
|
||||
});
|
||||
// Finally add the socket so it can start communicating with the client.
|
||||
AddInternalSocket(tcp_socket, info.proto);
|
||||
}
|
||||
}
|
||||
|
||||
void TurnServer::OnInternalSocketClose(rtc::AsyncPacketSocket* socket,
|
||||
int err) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
DestroyInternalSocket(socket);
|
||||
}
|
||||
|
||||
void TurnServer::OnInternalPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
// Fail if the packet is too small to even contain a channel header.
|
||||
if (packet.payload().size() < TURN_CHANNEL_HEADER_SIZE) {
|
||||
return;
|
||||
}
|
||||
InternalSocketMap::iterator iter = server_sockets_.find(socket);
|
||||
RTC_DCHECK(iter != server_sockets_.end());
|
||||
TurnServerConnection conn(packet.source_address(), iter->second, socket);
|
||||
uint16_t msg_type = rtc::GetBE16(packet.payload().data());
|
||||
if (!IsTurnChannelData(msg_type)) {
|
||||
// This is a STUN message.
|
||||
HandleStunMessage(&conn, packet.payload());
|
||||
} else {
|
||||
// This is a channel message; let the allocation handle it.
|
||||
TurnServerAllocation* allocation = FindAllocation(&conn);
|
||||
if (allocation) {
|
||||
allocation->HandleChannelData(packet.payload());
|
||||
}
|
||||
if (stun_message_observer_ != nullptr) {
|
||||
stun_message_observer_->ReceivedChannelData(packet.payload());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TurnServer::HandleStunMessage(TurnServerConnection* conn,
|
||||
rtc::ArrayView<const uint8_t> payload) {
|
||||
TurnMessage msg;
|
||||
rtc::ByteBufferReader buf(payload);
|
||||
if (!msg.Read(&buf) || (buf.Length() > 0)) {
|
||||
RTC_LOG(LS_WARNING) << "Received invalid STUN message";
|
||||
return;
|
||||
}
|
||||
|
||||
if (stun_message_observer_ != nullptr) {
|
||||
stun_message_observer_->ReceivedMessage(&msg);
|
||||
}
|
||||
|
||||
// If it's a STUN binding request, handle that specially.
|
||||
if (msg.type() == STUN_BINDING_REQUEST) {
|
||||
HandleBindingRequest(conn, &msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (redirect_hook_ != NULL && msg.type() == STUN_ALLOCATE_REQUEST) {
|
||||
rtc::SocketAddress address;
|
||||
if (redirect_hook_->ShouldRedirect(conn->src(), &address)) {
|
||||
SendErrorResponseWithAlternateServer(conn, &msg, address);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the key that we'll use to validate the M-I. If we have an
|
||||
// existing allocation, the key will already be cached.
|
||||
TurnServerAllocation* allocation = FindAllocation(conn);
|
||||
std::string key;
|
||||
if (!allocation) {
|
||||
GetKey(&msg, &key);
|
||||
} else {
|
||||
key = allocation->key();
|
||||
}
|
||||
|
||||
// Ensure the message is authorized; only needed for requests.
|
||||
if (IsStunRequestType(msg.type())) {
|
||||
if (!CheckAuthorization(conn, &msg, key)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!allocation && msg.type() == STUN_ALLOCATE_REQUEST) {
|
||||
HandleAllocateRequest(conn, &msg, key);
|
||||
} else if (allocation &&
|
||||
(msg.type() != STUN_ALLOCATE_REQUEST ||
|
||||
msg.transaction_id() == allocation->transaction_id())) {
|
||||
// This is a non-allocate request, or a retransmit of an allocate.
|
||||
// Check that the username matches the previous username used.
|
||||
if (IsStunRequestType(msg.type()) &&
|
||||
msg.GetByteString(STUN_ATTR_USERNAME)->string_view() !=
|
||||
allocation->username()) {
|
||||
SendErrorResponse(conn, &msg, STUN_ERROR_WRONG_CREDENTIALS,
|
||||
STUN_ERROR_REASON_WRONG_CREDENTIALS);
|
||||
return;
|
||||
}
|
||||
allocation->HandleTurnMessage(&msg);
|
||||
} else {
|
||||
// Allocation mismatch.
|
||||
SendErrorResponse(conn, &msg, STUN_ERROR_ALLOCATION_MISMATCH,
|
||||
STUN_ERROR_REASON_ALLOCATION_MISMATCH);
|
||||
}
|
||||
}
|
||||
|
||||
bool TurnServer::GetKey(const StunMessage* msg, std::string* key) {
|
||||
const StunByteStringAttribute* username_attr =
|
||||
msg->GetByteString(STUN_ATTR_USERNAME);
|
||||
if (!username_attr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (auth_hook_ != NULL &&
|
||||
auth_hook_->GetKey(std::string(username_attr->string_view()), realm_,
|
||||
key));
|
||||
}
|
||||
|
||||
bool TurnServer::CheckAuthorization(TurnServerConnection* conn,
|
||||
StunMessage* msg,
|
||||
absl::string_view key) {
|
||||
// RFC 5389, 10.2.2.
|
||||
RTC_DCHECK(IsStunRequestType(msg->type()));
|
||||
const StunByteStringAttribute* mi_attr =
|
||||
msg->GetByteString(STUN_ATTR_MESSAGE_INTEGRITY);
|
||||
const StunByteStringAttribute* username_attr =
|
||||
msg->GetByteString(STUN_ATTR_USERNAME);
|
||||
const StunByteStringAttribute* realm_attr =
|
||||
msg->GetByteString(STUN_ATTR_REALM);
|
||||
const StunByteStringAttribute* nonce_attr =
|
||||
msg->GetByteString(STUN_ATTR_NONCE);
|
||||
|
||||
// Fail if no MESSAGE_INTEGRITY.
|
||||
if (!mi_attr) {
|
||||
SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_UNAUTHORIZED,
|
||||
STUN_ERROR_REASON_UNAUTHORIZED);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fail if there is MESSAGE_INTEGRITY but no username, nonce, or realm.
|
||||
if (!username_attr || !realm_attr || !nonce_attr) {
|
||||
SendErrorResponse(conn, msg, STUN_ERROR_BAD_REQUEST,
|
||||
STUN_ERROR_REASON_BAD_REQUEST);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fail if bad nonce.
|
||||
if (!ValidateNonce(nonce_attr->string_view())) {
|
||||
SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_STALE_NONCE,
|
||||
STUN_ERROR_REASON_STALE_NONCE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fail if bad MESSAGE_INTEGRITY.
|
||||
if (key.empty() || msg->ValidateMessageIntegrity(std::string(key)) !=
|
||||
StunMessage::IntegrityStatus::kIntegrityOk) {
|
||||
SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_UNAUTHORIZED,
|
||||
STUN_ERROR_REASON_UNAUTHORIZED);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fail if one-time-use nonce feature is enabled.
|
||||
TurnServerAllocation* allocation = FindAllocation(conn);
|
||||
if (enable_otu_nonce_ && allocation &&
|
||||
allocation->last_nonce() == nonce_attr->string_view()) {
|
||||
SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_STALE_NONCE,
|
||||
STUN_ERROR_REASON_STALE_NONCE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (allocation) {
|
||||
allocation->set_last_nonce(nonce_attr->string_view());
|
||||
}
|
||||
// Success.
|
||||
return true;
|
||||
}
|
||||
|
||||
void TurnServer::HandleBindingRequest(TurnServerConnection* conn,
|
||||
const StunMessage* req) {
|
||||
StunMessage response(GetStunSuccessResponseTypeOrZero(*req),
|
||||
req->transaction_id());
|
||||
// Tell the user the address that we received their request from.
|
||||
auto mapped_addr_attr = std::make_unique<StunXorAddressAttribute>(
|
||||
STUN_ATTR_XOR_MAPPED_ADDRESS, conn->src());
|
||||
response.AddAttribute(std::move(mapped_addr_attr));
|
||||
|
||||
SendStun(conn, &response);
|
||||
}
|
||||
|
||||
void TurnServer::HandleAllocateRequest(TurnServerConnection* conn,
|
||||
const TurnMessage* msg,
|
||||
absl::string_view key) {
|
||||
// Check the parameters in the request.
|
||||
const StunUInt32Attribute* transport_attr =
|
||||
msg->GetUInt32(STUN_ATTR_REQUESTED_TRANSPORT);
|
||||
if (!transport_attr) {
|
||||
SendErrorResponse(conn, msg, STUN_ERROR_BAD_REQUEST,
|
||||
STUN_ERROR_REASON_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only UDP is supported right now.
|
||||
int proto = transport_attr->value() >> 24;
|
||||
if (proto != IPPROTO_UDP) {
|
||||
SendErrorResponse(conn, msg, STUN_ERROR_UNSUPPORTED_PROTOCOL,
|
||||
STUN_ERROR_REASON_UNSUPPORTED_PROTOCOL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the allocation and let it send the success response.
|
||||
// If the actual socket allocation fails, send an internal error.
|
||||
TurnServerAllocation* alloc = CreateAllocation(conn, proto, key);
|
||||
if (alloc) {
|
||||
alloc->HandleTurnMessage(msg);
|
||||
} else {
|
||||
SendErrorResponse(conn, msg, STUN_ERROR_SERVER_ERROR,
|
||||
"Failed to allocate socket");
|
||||
}
|
||||
}
|
||||
|
||||
std::string TurnServer::GenerateNonce(int64_t now) const {
|
||||
// Generate a nonce of the form hex(now + HMAC-MD5(nonce_key_, now))
|
||||
std::string input(reinterpret_cast<const char*>(&now), sizeof(now));
|
||||
std::string nonce = rtc::hex_encode(input);
|
||||
nonce += rtc::ComputeHmac(rtc::DIGEST_MD5, nonce_key_, input);
|
||||
RTC_DCHECK(nonce.size() == kNonceSize);
|
||||
|
||||
return nonce;
|
||||
}
|
||||
|
||||
bool TurnServer::ValidateNonce(absl::string_view nonce) const {
|
||||
// Check the size.
|
||||
if (nonce.size() != kNonceSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decode the timestamp.
|
||||
int64_t then;
|
||||
char* p = reinterpret_cast<char*>(&then);
|
||||
size_t len = rtc::hex_decode(rtc::ArrayView<char>(p, sizeof(then)),
|
||||
nonce.substr(0, sizeof(then) * 2));
|
||||
if (len != sizeof(then)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify the HMAC.
|
||||
if (nonce.substr(sizeof(then) * 2) !=
|
||||
rtc::ComputeHmac(rtc::DIGEST_MD5, nonce_key_,
|
||||
std::string(p, sizeof(then)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate the timestamp.
|
||||
return TimeDelta::Millis(rtc::TimeMillis() - then) < kNonceTimeout;
|
||||
}
|
||||
|
||||
TurnServerAllocation* TurnServer::FindAllocation(TurnServerConnection* conn) {
|
||||
AllocationMap::const_iterator it = allocations_.find(*conn);
|
||||
return (it != allocations_.end()) ? it->second.get() : nullptr;
|
||||
}
|
||||
|
||||
TurnServerAllocation* TurnServer::CreateAllocation(TurnServerConnection* conn,
|
||||
int proto,
|
||||
absl::string_view key) {
|
||||
rtc::AsyncPacketSocket* external_socket =
|
||||
(external_socket_factory_)
|
||||
? external_socket_factory_->CreateUdpSocket(external_addr_, 0, 0)
|
||||
: NULL;
|
||||
if (!external_socket) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// The Allocation takes ownership of the socket.
|
||||
TurnServerAllocation* allocation =
|
||||
new TurnServerAllocation(this, thread_, *conn, external_socket, key);
|
||||
allocations_[*conn].reset(allocation);
|
||||
return allocation;
|
||||
}
|
||||
|
||||
void TurnServer::SendErrorResponse(TurnServerConnection* conn,
|
||||
const StunMessage* req,
|
||||
int code,
|
||||
absl::string_view reason) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
TurnMessage resp(GetStunErrorResponseTypeOrZero(*req), req->transaction_id());
|
||||
InitErrorResponse(code, reason, &resp);
|
||||
|
||||
RTC_LOG(LS_INFO) << "Sending error response, type=" << resp.type()
|
||||
<< ", code=" << code << ", reason=" << reason;
|
||||
SendStun(conn, &resp);
|
||||
}
|
||||
|
||||
void TurnServer::SendErrorResponseWithRealmAndNonce(TurnServerConnection* conn,
|
||||
const StunMessage* msg,
|
||||
int code,
|
||||
absl::string_view reason) {
|
||||
TurnMessage resp(GetStunErrorResponseTypeOrZero(*msg), msg->transaction_id());
|
||||
InitErrorResponse(code, reason, &resp);
|
||||
|
||||
int64_t timestamp = rtc::TimeMillis();
|
||||
if (ts_for_next_nonce_) {
|
||||
timestamp = ts_for_next_nonce_;
|
||||
ts_for_next_nonce_ = 0;
|
||||
}
|
||||
resp.AddAttribute(std::make_unique<StunByteStringAttribute>(
|
||||
STUN_ATTR_NONCE, GenerateNonce(timestamp)));
|
||||
resp.AddAttribute(
|
||||
std::make_unique<StunByteStringAttribute>(STUN_ATTR_REALM, realm_));
|
||||
SendStun(conn, &resp);
|
||||
}
|
||||
|
||||
void TurnServer::SendErrorResponseWithAlternateServer(
|
||||
TurnServerConnection* conn,
|
||||
const StunMessage* msg,
|
||||
const rtc::SocketAddress& addr) {
|
||||
TurnMessage resp(GetStunErrorResponseTypeOrZero(*msg), msg->transaction_id());
|
||||
InitErrorResponse(STUN_ERROR_TRY_ALTERNATE,
|
||||
STUN_ERROR_REASON_TRY_ALTERNATE_SERVER, &resp);
|
||||
resp.AddAttribute(
|
||||
std::make_unique<StunAddressAttribute>(STUN_ATTR_ALTERNATE_SERVER, addr));
|
||||
SendStun(conn, &resp);
|
||||
}
|
||||
|
||||
void TurnServer::SendStun(TurnServerConnection* conn, StunMessage* msg) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
rtc::ByteBufferWriter buf;
|
||||
// Add a SOFTWARE attribute if one is set.
|
||||
if (!software_.empty()) {
|
||||
msg->AddAttribute(std::make_unique<StunByteStringAttribute>(
|
||||
STUN_ATTR_SOFTWARE, software_));
|
||||
}
|
||||
msg->Write(&buf);
|
||||
Send(conn, buf);
|
||||
}
|
||||
|
||||
void TurnServer::Send(TurnServerConnection* conn,
|
||||
const rtc::ByteBufferWriter& buf) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
rtc::PacketOptions options;
|
||||
conn->socket()->SendTo(buf.Data(), buf.Length(), conn->src(), options);
|
||||
}
|
||||
|
||||
void TurnServer::DestroyAllocation(TurnServerAllocation* allocation) {
|
||||
// Removing the internal socket if the connection is not udp.
|
||||
rtc::AsyncPacketSocket* socket = allocation->conn()->socket();
|
||||
InternalSocketMap::iterator iter = server_sockets_.find(socket);
|
||||
// Skip if the socket serving this allocation is UDP, as this will be shared
|
||||
// by all allocations.
|
||||
// Note: We may not find a socket if it's a TCP socket that was closed, and
|
||||
// the allocation is only now timing out.
|
||||
if (iter != server_sockets_.end() && iter->second != cricket::PROTO_UDP) {
|
||||
DestroyInternalSocket(socket);
|
||||
}
|
||||
|
||||
allocations_.erase(*(allocation->conn()));
|
||||
}
|
||||
|
||||
void TurnServer::DestroyInternalSocket(rtc::AsyncPacketSocket* socket) {
|
||||
InternalSocketMap::iterator iter = server_sockets_.find(socket);
|
||||
if (iter != server_sockets_.end()) {
|
||||
rtc::AsyncPacketSocket* socket = iter->first;
|
||||
socket->UnsubscribeCloseEvent(this);
|
||||
socket->DeregisterReceivedPacketCallback();
|
||||
server_sockets_.erase(iter);
|
||||
std::unique_ptr<rtc::AsyncPacketSocket> socket_to_delete =
|
||||
absl::WrapUnique(socket);
|
||||
// We must destroy the socket async to avoid invalidating the sigslot
|
||||
// callback list iterator inside a sigslot callback. (In other words,
|
||||
// deleting an object from within a callback from that object).
|
||||
thread_->PostTask([socket_to_delete = std::move(socket_to_delete)] {});
|
||||
}
|
||||
}
|
||||
|
||||
TurnServerConnection::TurnServerConnection(const rtc::SocketAddress& src,
|
||||
ProtocolType proto,
|
||||
rtc::AsyncPacketSocket* socket)
|
||||
: src_(src),
|
||||
dst_(socket->GetRemoteAddress()),
|
||||
proto_(proto),
|
||||
socket_(socket) {}
|
||||
|
||||
bool TurnServerConnection::operator==(const TurnServerConnection& c) const {
|
||||
return src_ == c.src_ && dst_ == c.dst_ && proto_ == c.proto_;
|
||||
}
|
||||
|
||||
bool TurnServerConnection::operator<(const TurnServerConnection& c) const {
|
||||
return std::tie(src_, dst_, proto_) < std::tie(c.src_, c.dst_, c.proto_);
|
||||
}
|
||||
|
||||
std::string TurnServerConnection::ToString() const {
|
||||
const char* const kProtos[] = {"unknown", "udp", "tcp", "ssltcp"};
|
||||
rtc::StringBuilder ost;
|
||||
ost << src_.ToSensitiveString() << "-" << dst_.ToSensitiveString() << ":"
|
||||
<< kProtos[proto_];
|
||||
return ost.Release();
|
||||
}
|
||||
|
||||
TurnServerAllocation::TurnServerAllocation(TurnServer* server,
|
||||
webrtc::TaskQueueBase* thread,
|
||||
const TurnServerConnection& conn,
|
||||
rtc::AsyncPacketSocket* socket,
|
||||
absl::string_view key)
|
||||
: server_(server),
|
||||
thread_(thread),
|
||||
conn_(conn),
|
||||
external_socket_(socket),
|
||||
key_(key) {
|
||||
external_socket_->RegisterReceivedPacketCallback(
|
||||
[&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
OnExternalPacket(socket, packet);
|
||||
});
|
||||
}
|
||||
|
||||
TurnServerAllocation::~TurnServerAllocation() {
|
||||
channels_.clear();
|
||||
perms_.clear();
|
||||
RTC_LOG(LS_INFO) << ToString() << ": Allocation destroyed";
|
||||
}
|
||||
|
||||
std::string TurnServerAllocation::ToString() const {
|
||||
rtc::StringBuilder ost;
|
||||
ost << "Alloc[" << conn_.ToString() << "]";
|
||||
return ost.Release();
|
||||
}
|
||||
|
||||
void TurnServerAllocation::HandleTurnMessage(const TurnMessage* msg) {
|
||||
RTC_DCHECK(msg != NULL);
|
||||
switch (msg->type()) {
|
||||
case STUN_ALLOCATE_REQUEST:
|
||||
HandleAllocateRequest(msg);
|
||||
break;
|
||||
case TURN_REFRESH_REQUEST:
|
||||
HandleRefreshRequest(msg);
|
||||
break;
|
||||
case TURN_SEND_INDICATION:
|
||||
HandleSendIndication(msg);
|
||||
break;
|
||||
case TURN_CREATE_PERMISSION_REQUEST:
|
||||
HandleCreatePermissionRequest(msg);
|
||||
break;
|
||||
case TURN_CHANNEL_BIND_REQUEST:
|
||||
HandleChannelBindRequest(msg);
|
||||
break;
|
||||
default:
|
||||
// Not sure what to do with this, just eat it.
|
||||
RTC_LOG(LS_WARNING) << ToString()
|
||||
<< ": Invalid TURN message type received: "
|
||||
<< msg->type();
|
||||
}
|
||||
}
|
||||
|
||||
void TurnServerAllocation::HandleAllocateRequest(const TurnMessage* msg) {
|
||||
// Copy the important info from the allocate request.
|
||||
transaction_id_ = msg->transaction_id();
|
||||
const StunByteStringAttribute* username_attr =
|
||||
msg->GetByteString(STUN_ATTR_USERNAME);
|
||||
RTC_DCHECK(username_attr != NULL);
|
||||
username_ = std::string(username_attr->string_view());
|
||||
|
||||
// Figure out the lifetime and start the allocation timer.
|
||||
TimeDelta lifetime = ComputeLifetime(*msg);
|
||||
PostDeleteSelf(lifetime);
|
||||
|
||||
RTC_LOG(LS_INFO) << ToString() << ": Created allocation with lifetime="
|
||||
<< lifetime.seconds();
|
||||
|
||||
// We've already validated all the important bits; just send a response here.
|
||||
TurnMessage response(GetStunSuccessResponseTypeOrZero(*msg),
|
||||
msg->transaction_id());
|
||||
|
||||
auto mapped_addr_attr = std::make_unique<StunXorAddressAttribute>(
|
||||
STUN_ATTR_XOR_MAPPED_ADDRESS, conn_.src());
|
||||
auto relayed_addr_attr = std::make_unique<StunXorAddressAttribute>(
|
||||
STUN_ATTR_XOR_RELAYED_ADDRESS, external_socket_->GetLocalAddress());
|
||||
auto lifetime_attr = std::make_unique<StunUInt32Attribute>(
|
||||
STUN_ATTR_LIFETIME, lifetime.seconds());
|
||||
response.AddAttribute(std::move(mapped_addr_attr));
|
||||
response.AddAttribute(std::move(relayed_addr_attr));
|
||||
response.AddAttribute(std::move(lifetime_attr));
|
||||
|
||||
SendResponse(&response);
|
||||
}
|
||||
|
||||
void TurnServerAllocation::HandleRefreshRequest(const TurnMessage* msg) {
|
||||
// Figure out the new lifetime.
|
||||
TimeDelta lifetime = ComputeLifetime(*msg);
|
||||
|
||||
// Reset the expiration timer.
|
||||
safety_.reset();
|
||||
PostDeleteSelf(lifetime);
|
||||
|
||||
RTC_LOG(LS_INFO) << ToString()
|
||||
<< ": Refreshed allocation, lifetime=" << lifetime.seconds();
|
||||
|
||||
// Send a success response with a LIFETIME attribute.
|
||||
TurnMessage response(GetStunSuccessResponseTypeOrZero(*msg),
|
||||
msg->transaction_id());
|
||||
|
||||
auto lifetime_attr = std::make_unique<StunUInt32Attribute>(
|
||||
STUN_ATTR_LIFETIME, lifetime.seconds());
|
||||
response.AddAttribute(std::move(lifetime_attr));
|
||||
|
||||
SendResponse(&response);
|
||||
}
|
||||
|
||||
void TurnServerAllocation::HandleSendIndication(const TurnMessage* msg) {
|
||||
// Check mandatory attributes.
|
||||
const StunByteStringAttribute* data_attr = msg->GetByteString(STUN_ATTR_DATA);
|
||||
const StunAddressAttribute* peer_attr =
|
||||
msg->GetAddress(STUN_ATTR_XOR_PEER_ADDRESS);
|
||||
if (!data_attr || !peer_attr) {
|
||||
RTC_LOG(LS_WARNING) << ToString() << ": Received invalid send indication";
|
||||
return;
|
||||
}
|
||||
|
||||
// If a permission exists, send the data on to the peer.
|
||||
if (HasPermission(peer_attr->GetAddress().ipaddr())) {
|
||||
SendExternal(reinterpret_cast<char*>(data_attr->array_view().data()),
|
||||
data_attr->length(), peer_attr->GetAddress());
|
||||
} else {
|
||||
RTC_LOG(LS_WARNING) << ToString()
|
||||
<< ": Received send indication without permission"
|
||||
" peer="
|
||||
<< peer_attr->GetAddress().ToSensitiveString();
|
||||
}
|
||||
}
|
||||
|
||||
void TurnServerAllocation::HandleCreatePermissionRequest(
|
||||
const TurnMessage* msg) {
|
||||
// Check mandatory attributes.
|
||||
const StunAddressAttribute* peer_attr =
|
||||
msg->GetAddress(STUN_ATTR_XOR_PEER_ADDRESS);
|
||||
if (!peer_attr) {
|
||||
SendBadRequestResponse(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (server_->reject_private_addresses_ &&
|
||||
rtc::IPIsPrivate(peer_attr->GetAddress().ipaddr())) {
|
||||
SendErrorResponse(msg, STUN_ERROR_FORBIDDEN, STUN_ERROR_REASON_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add this permission.
|
||||
AddPermission(peer_attr->GetAddress().ipaddr());
|
||||
|
||||
RTC_LOG(LS_INFO) << ToString() << ": Created permission, peer="
|
||||
<< peer_attr->GetAddress().ToSensitiveString();
|
||||
|
||||
// Send a success response.
|
||||
TurnMessage response(GetStunSuccessResponseTypeOrZero(*msg),
|
||||
msg->transaction_id());
|
||||
SendResponse(&response);
|
||||
}
|
||||
|
||||
void TurnServerAllocation::HandleChannelBindRequest(const TurnMessage* msg) {
|
||||
// Check mandatory attributes.
|
||||
const StunUInt32Attribute* channel_attr =
|
||||
msg->GetUInt32(STUN_ATTR_CHANNEL_NUMBER);
|
||||
const StunAddressAttribute* peer_attr =
|
||||
msg->GetAddress(STUN_ATTR_XOR_PEER_ADDRESS);
|
||||
if (!channel_attr || !peer_attr) {
|
||||
SendBadRequestResponse(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that channel id is valid.
|
||||
int channel_id = channel_attr->value() >> 16;
|
||||
if (channel_id < kMinChannelNumber || channel_id > kMaxChannelNumber) {
|
||||
SendBadRequestResponse(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that this channel id isn't bound to another transport address, and
|
||||
// that this transport address isn't bound to another channel id.
|
||||
auto channel1 = FindChannel(channel_id);
|
||||
auto channel2 = FindChannel(peer_attr->GetAddress());
|
||||
if (channel1 != channel2) {
|
||||
SendBadRequestResponse(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add or refresh this channel.
|
||||
if (channel1 == channels_.end()) {
|
||||
channel1 = channels_.insert(
|
||||
channels_.end(), {.id = channel_id, .peer = peer_attr->GetAddress()});
|
||||
} else {
|
||||
channel1->pending_delete.reset();
|
||||
}
|
||||
thread_->PostDelayedTask(
|
||||
SafeTask(channel1->pending_delete.flag(),
|
||||
[this, channel1] { channels_.erase(channel1); }),
|
||||
kChannelTimeout);
|
||||
|
||||
// Channel binds also refresh permissions.
|
||||
AddPermission(peer_attr->GetAddress().ipaddr());
|
||||
|
||||
RTC_LOG(LS_INFO) << ToString() << ": Bound channel, id=" << channel_id
|
||||
<< ", peer=" << peer_attr->GetAddress().ToSensitiveString();
|
||||
|
||||
// Send a success response.
|
||||
TurnMessage response(GetStunSuccessResponseTypeOrZero(*msg),
|
||||
msg->transaction_id());
|
||||
SendResponse(&response);
|
||||
}
|
||||
|
||||
void TurnServerAllocation::HandleChannelData(
|
||||
rtc::ArrayView<const uint8_t> payload) {
|
||||
// Extract the channel number from the data.
|
||||
uint16_t channel_id = rtc::GetBE16(payload.data());
|
||||
auto channel = FindChannel(channel_id);
|
||||
if (channel != channels_.end()) {
|
||||
// Send the data to the peer address.
|
||||
SendExternal(payload.data() + TURN_CHANNEL_HEADER_SIZE,
|
||||
payload.size() - TURN_CHANNEL_HEADER_SIZE, channel->peer);
|
||||
} else {
|
||||
RTC_LOG(LS_WARNING) << ToString()
|
||||
<< ": Received channel data for invalid channel, id="
|
||||
<< channel_id;
|
||||
}
|
||||
}
|
||||
|
||||
void TurnServerAllocation::OnExternalPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet) {
|
||||
RTC_DCHECK(external_socket_.get() == socket);
|
||||
auto channel = FindChannel(packet.source_address());
|
||||
if (channel != channels_.end()) {
|
||||
// There is a channel bound to this address. Send as a channel message.
|
||||
rtc::ByteBufferWriter buf;
|
||||
buf.WriteUInt16(channel->id);
|
||||
buf.WriteUInt16(static_cast<uint16_t>(packet.payload().size()));
|
||||
buf.WriteBytes(packet.payload().data(), packet.payload().size());
|
||||
server_->Send(&conn_, buf);
|
||||
} else if (!server_->enable_permission_checks_ ||
|
||||
HasPermission(packet.source_address().ipaddr())) {
|
||||
// No channel, but a permission exists. Send as a data indication.
|
||||
TurnMessage msg(TURN_DATA_INDICATION);
|
||||
msg.AddAttribute(std::make_unique<StunXorAddressAttribute>(
|
||||
STUN_ATTR_XOR_PEER_ADDRESS, packet.source_address()));
|
||||
msg.AddAttribute(std::make_unique<StunByteStringAttribute>(
|
||||
STUN_ATTR_DATA, packet.payload().data(), packet.payload().size()));
|
||||
server_->SendStun(&conn_, &msg);
|
||||
} else {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< ToString() << ": Received external packet without permission, peer="
|
||||
<< packet.source_address().ToSensitiveString();
|
||||
}
|
||||
}
|
||||
|
||||
TimeDelta TurnServerAllocation::ComputeLifetime(const TurnMessage& msg) {
|
||||
if (const StunUInt32Attribute* attr = msg.GetUInt32(STUN_ATTR_LIFETIME)) {
|
||||
return std::min(TimeDelta::Seconds(static_cast<int>(attr->value())),
|
||||
kDefaultAllocationTimeout);
|
||||
}
|
||||
return kDefaultAllocationTimeout;
|
||||
}
|
||||
|
||||
bool TurnServerAllocation::HasPermission(const rtc::IPAddress& addr) {
|
||||
return FindPermission(addr) != perms_.end();
|
||||
}
|
||||
|
||||
void TurnServerAllocation::AddPermission(const rtc::IPAddress& addr) {
|
||||
auto perm = FindPermission(addr);
|
||||
if (perm == perms_.end()) {
|
||||
perm = perms_.insert(perms_.end(), {.peer = addr});
|
||||
} else {
|
||||
perm->pending_delete.reset();
|
||||
}
|
||||
thread_->PostDelayedTask(SafeTask(perm->pending_delete.flag(),
|
||||
[this, perm] { perms_.erase(perm); }),
|
||||
kPermissionTimeout);
|
||||
}
|
||||
|
||||
TurnServerAllocation::PermissionList::iterator
|
||||
TurnServerAllocation::FindPermission(const rtc::IPAddress& addr) {
|
||||
return absl::c_find_if(perms_,
|
||||
[&](const Permission& p) { return p.peer == addr; });
|
||||
}
|
||||
|
||||
TurnServerAllocation::ChannelList::iterator TurnServerAllocation::FindChannel(
|
||||
int channel_id) {
|
||||
return absl::c_find_if(channels_,
|
||||
[&](const Channel& c) { return c.id == channel_id; });
|
||||
}
|
||||
|
||||
TurnServerAllocation::ChannelList::iterator TurnServerAllocation::FindChannel(
|
||||
const rtc::SocketAddress& addr) {
|
||||
return absl::c_find_if(channels_,
|
||||
[&](const Channel& c) { return c.peer == addr; });
|
||||
}
|
||||
|
||||
void TurnServerAllocation::SendResponse(TurnMessage* msg) {
|
||||
// Success responses always have M-I.
|
||||
msg->AddMessageIntegrity(key_);
|
||||
server_->SendStun(&conn_, msg);
|
||||
}
|
||||
|
||||
void TurnServerAllocation::SendBadRequestResponse(const TurnMessage* req) {
|
||||
SendErrorResponse(req, STUN_ERROR_BAD_REQUEST, STUN_ERROR_REASON_BAD_REQUEST);
|
||||
}
|
||||
|
||||
void TurnServerAllocation::SendErrorResponse(const TurnMessage* req,
|
||||
int code,
|
||||
absl::string_view reason) {
|
||||
server_->SendErrorResponse(&conn_, req, code, reason);
|
||||
}
|
||||
|
||||
void TurnServerAllocation::SendExternal(const void* data,
|
||||
size_t size,
|
||||
const rtc::SocketAddress& peer) {
|
||||
rtc::PacketOptions options;
|
||||
external_socket_->SendTo(data, size, peer, options);
|
||||
}
|
||||
|
||||
void TurnServerAllocation::PostDeleteSelf(TimeDelta delay) {
|
||||
auto delete_self = [this] {
|
||||
RTC_DCHECK_RUN_ON(server_->thread_);
|
||||
server_->DestroyAllocation(this);
|
||||
};
|
||||
thread_->PostDelayedTask(SafeTask(safety_.flag(), std::move(delete_self)),
|
||||
delay);
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
366
TMessagesProj/jni/voip/webrtc/p2p/base/turn_server.h
Normal file
366
TMessagesProj/jni/voip/webrtc/p2p/base/turn_server.h
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
* Copyright 2012 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_TURN_SERVER_H_
|
||||
#define P2P_BASE_TURN_SERVER_H_
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "p2p/base/port_interface.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/network/received_packet.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/ssl_adapter.h"
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
|
||||
namespace rtc {
|
||||
class ByteBufferWriter;
|
||||
class PacketSocketFactory;
|
||||
} // namespace rtc
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class StunMessage;
|
||||
class TurnMessage;
|
||||
class TurnServer;
|
||||
|
||||
// The default server port for TURN, as specified in RFC5766.
|
||||
const int TURN_SERVER_PORT = 3478;
|
||||
|
||||
// Encapsulates the client's connection to the server.
|
||||
class TurnServerConnection {
|
||||
public:
|
||||
TurnServerConnection() : proto_(PROTO_UDP), socket_(NULL) {}
|
||||
TurnServerConnection(const rtc::SocketAddress& src,
|
||||
ProtocolType proto,
|
||||
rtc::AsyncPacketSocket* socket);
|
||||
const rtc::SocketAddress& src() const { return src_; }
|
||||
rtc::AsyncPacketSocket* socket() { return socket_; }
|
||||
bool operator==(const TurnServerConnection& t) const;
|
||||
bool operator<(const TurnServerConnection& t) const;
|
||||
std::string ToString() const;
|
||||
|
||||
private:
|
||||
rtc::SocketAddress src_;
|
||||
rtc::SocketAddress dst_;
|
||||
cricket::ProtocolType proto_;
|
||||
rtc::AsyncPacketSocket* socket_;
|
||||
};
|
||||
|
||||
// Encapsulates a TURN allocation.
|
||||
// The object is created when an allocation request is received, and then
|
||||
// handles TURN messages (via HandleTurnMessage) and channel data messages
|
||||
// (via HandleChannelData) for this allocation when received by the server.
|
||||
// The object informs the server when its lifetime timer expires.
|
||||
class TurnServerAllocation {
|
||||
public:
|
||||
TurnServerAllocation(TurnServer* server_,
|
||||
webrtc::TaskQueueBase* thread,
|
||||
const TurnServerConnection& conn,
|
||||
rtc::AsyncPacketSocket* server_socket,
|
||||
absl::string_view key);
|
||||
virtual ~TurnServerAllocation();
|
||||
|
||||
TurnServerConnection* conn() { return &conn_; }
|
||||
const std::string& key() const { return key_; }
|
||||
const std::string& transaction_id() const { return transaction_id_; }
|
||||
const std::string& username() const { return username_; }
|
||||
const std::string& last_nonce() const { return last_nonce_; }
|
||||
void set_last_nonce(absl::string_view nonce) {
|
||||
last_nonce_ = std::string(nonce);
|
||||
}
|
||||
|
||||
std::string ToString() const;
|
||||
|
||||
void HandleTurnMessage(const TurnMessage* msg);
|
||||
void HandleChannelData(rtc::ArrayView<const uint8_t> payload);
|
||||
|
||||
private:
|
||||
struct Channel {
|
||||
webrtc::ScopedTaskSafety pending_delete;
|
||||
int id;
|
||||
rtc::SocketAddress peer;
|
||||
};
|
||||
struct Permission {
|
||||
webrtc::ScopedTaskSafety pending_delete;
|
||||
rtc::IPAddress peer;
|
||||
};
|
||||
using PermissionList = std::list<Permission>;
|
||||
using ChannelList = std::list<Channel>;
|
||||
|
||||
void PostDeleteSelf(webrtc::TimeDelta delay);
|
||||
|
||||
void HandleAllocateRequest(const TurnMessage* msg);
|
||||
void HandleRefreshRequest(const TurnMessage* msg);
|
||||
void HandleSendIndication(const TurnMessage* msg);
|
||||
void HandleCreatePermissionRequest(const TurnMessage* msg);
|
||||
void HandleChannelBindRequest(const TurnMessage* msg);
|
||||
|
||||
void OnExternalPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet);
|
||||
|
||||
static webrtc::TimeDelta ComputeLifetime(const TurnMessage& msg);
|
||||
bool HasPermission(const rtc::IPAddress& addr);
|
||||
void AddPermission(const rtc::IPAddress& addr);
|
||||
PermissionList::iterator FindPermission(const rtc::IPAddress& addr);
|
||||
ChannelList::iterator FindChannel(int channel_id);
|
||||
ChannelList::iterator FindChannel(const rtc::SocketAddress& addr);
|
||||
|
||||
void SendResponse(TurnMessage* msg);
|
||||
void SendBadRequestResponse(const TurnMessage* req);
|
||||
void SendErrorResponse(const TurnMessage* req,
|
||||
int code,
|
||||
absl::string_view reason);
|
||||
void SendExternal(const void* data,
|
||||
size_t size,
|
||||
const rtc::SocketAddress& peer);
|
||||
|
||||
TurnServer* const server_;
|
||||
webrtc::TaskQueueBase* const thread_;
|
||||
TurnServerConnection conn_;
|
||||
std::unique_ptr<rtc::AsyncPacketSocket> external_socket_;
|
||||
std::string key_;
|
||||
std::string transaction_id_;
|
||||
std::string username_;
|
||||
std::string last_nonce_;
|
||||
PermissionList perms_;
|
||||
ChannelList channels_;
|
||||
webrtc::ScopedTaskSafety safety_;
|
||||
};
|
||||
|
||||
// An interface through which the MD5 credential hash can be retrieved.
|
||||
class TurnAuthInterface {
|
||||
public:
|
||||
// Gets HA1 for the specified user and realm.
|
||||
// HA1 = MD5(A1) = MD5(username:realm:password).
|
||||
// Return true if the given username and realm are valid, or false if not.
|
||||
virtual bool GetKey(absl::string_view username,
|
||||
absl::string_view realm,
|
||||
std::string* key) = 0;
|
||||
virtual ~TurnAuthInterface() = default;
|
||||
};
|
||||
|
||||
// An interface enables Turn Server to control redirection behavior.
|
||||
class TurnRedirectInterface {
|
||||
public:
|
||||
virtual bool ShouldRedirect(const rtc::SocketAddress& address,
|
||||
rtc::SocketAddress* out) = 0;
|
||||
virtual ~TurnRedirectInterface() {}
|
||||
};
|
||||
|
||||
class StunMessageObserver {
|
||||
public:
|
||||
virtual void ReceivedMessage(const TurnMessage* msg) = 0;
|
||||
virtual void ReceivedChannelData(rtc::ArrayView<const uint8_t> payload) = 0;
|
||||
virtual ~StunMessageObserver() {}
|
||||
};
|
||||
|
||||
// The core TURN server class. Give it a socket to listen on via
|
||||
// AddInternalServerSocket, and a factory to create external sockets via
|
||||
// SetExternalSocketFactory, and it's ready to go.
|
||||
// Not yet wired up: TCP support.
|
||||
class TurnServer : public sigslot::has_slots<> {
|
||||
public:
|
||||
typedef std::map<TurnServerConnection, std::unique_ptr<TurnServerAllocation>>
|
||||
AllocationMap;
|
||||
|
||||
explicit TurnServer(webrtc::TaskQueueBase* thread);
|
||||
~TurnServer() override;
|
||||
|
||||
// Gets/sets the realm value to use for the server.
|
||||
const std::string& realm() const {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
return realm_;
|
||||
}
|
||||
void set_realm(absl::string_view realm) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
realm_ = std::string(realm);
|
||||
}
|
||||
|
||||
// Gets/sets the value for the SOFTWARE attribute for TURN messages.
|
||||
const std::string& software() const {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
return software_;
|
||||
}
|
||||
void set_software(absl::string_view software) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
software_ = std::string(software);
|
||||
}
|
||||
|
||||
const AllocationMap& allocations() const {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
return allocations_;
|
||||
}
|
||||
|
||||
// Sets the authentication callback; does not take ownership.
|
||||
void set_auth_hook(TurnAuthInterface* auth_hook) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
auth_hook_ = auth_hook;
|
||||
}
|
||||
|
||||
void set_redirect_hook(TurnRedirectInterface* redirect_hook) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
redirect_hook_ = redirect_hook;
|
||||
}
|
||||
|
||||
void set_enable_otu_nonce(bool enable) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
enable_otu_nonce_ = enable;
|
||||
}
|
||||
|
||||
// If set to true, reject CreatePermission requests to RFC1918 addresses.
|
||||
void set_reject_private_addresses(bool filter) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
reject_private_addresses_ = filter;
|
||||
}
|
||||
|
||||
void set_enable_permission_checks(bool enable) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
enable_permission_checks_ = enable;
|
||||
}
|
||||
|
||||
// Starts listening for packets from internal clients.
|
||||
void AddInternalSocket(rtc::AsyncPacketSocket* socket, ProtocolType proto);
|
||||
// Starts listening for the connections on this socket. When someone tries
|
||||
// to connect, the connection will be accepted and a new internal socket
|
||||
// will be added.
|
||||
void AddInternalServerSocket(
|
||||
rtc::Socket* socket,
|
||||
ProtocolType proto,
|
||||
std::unique_ptr<rtc::SSLAdapterFactory> ssl_adapter_factory = nullptr);
|
||||
// Specifies the factory to use for creating external sockets.
|
||||
void SetExternalSocketFactory(rtc::PacketSocketFactory* factory,
|
||||
const rtc::SocketAddress& address);
|
||||
// For testing only.
|
||||
std::string SetTimestampForNextNonce(int64_t timestamp) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
ts_for_next_nonce_ = timestamp;
|
||||
return GenerateNonce(timestamp);
|
||||
}
|
||||
|
||||
void SetStunMessageObserver(std::unique_ptr<StunMessageObserver> observer) {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
stun_message_observer_ = std::move(observer);
|
||||
}
|
||||
|
||||
private:
|
||||
// All private member functions and variables should have access restricted to
|
||||
// thread_. But compile-time annotations are missing for members access from
|
||||
// TurnServerAllocation (via friend declaration).
|
||||
|
||||
std::string GenerateNonce(int64_t now) const RTC_RUN_ON(thread_);
|
||||
void OnInternalPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet) RTC_RUN_ON(thread_);
|
||||
|
||||
void OnNewInternalConnection(rtc::Socket* socket);
|
||||
|
||||
// Accept connections on this server socket.
|
||||
void AcceptConnection(rtc::Socket* server_socket) RTC_RUN_ON(thread_);
|
||||
void OnInternalSocketClose(rtc::AsyncPacketSocket* socket, int err);
|
||||
|
||||
void HandleStunMessage(TurnServerConnection* conn,
|
||||
rtc::ArrayView<const uint8_t> payload)
|
||||
RTC_RUN_ON(thread_);
|
||||
void HandleBindingRequest(TurnServerConnection* conn, const StunMessage* msg)
|
||||
RTC_RUN_ON(thread_);
|
||||
void HandleAllocateRequest(TurnServerConnection* conn,
|
||||
const TurnMessage* msg,
|
||||
absl::string_view key) RTC_RUN_ON(thread_);
|
||||
|
||||
bool GetKey(const StunMessage* msg, std::string* key) RTC_RUN_ON(thread_);
|
||||
bool CheckAuthorization(TurnServerConnection* conn,
|
||||
StunMessage* msg,
|
||||
absl::string_view key) RTC_RUN_ON(thread_);
|
||||
bool ValidateNonce(absl::string_view nonce) const RTC_RUN_ON(thread_);
|
||||
|
||||
TurnServerAllocation* FindAllocation(TurnServerConnection* conn)
|
||||
RTC_RUN_ON(thread_);
|
||||
TurnServerAllocation* CreateAllocation(TurnServerConnection* conn,
|
||||
int proto,
|
||||
absl::string_view key)
|
||||
RTC_RUN_ON(thread_);
|
||||
|
||||
void SendErrorResponse(TurnServerConnection* conn,
|
||||
const StunMessage* req,
|
||||
int code,
|
||||
absl::string_view reason);
|
||||
|
||||
void SendErrorResponseWithRealmAndNonce(TurnServerConnection* conn,
|
||||
const StunMessage* req,
|
||||
int code,
|
||||
absl::string_view reason)
|
||||
RTC_RUN_ON(thread_);
|
||||
|
||||
void SendErrorResponseWithAlternateServer(TurnServerConnection* conn,
|
||||
const StunMessage* req,
|
||||
const rtc::SocketAddress& addr)
|
||||
RTC_RUN_ON(thread_);
|
||||
|
||||
void SendStun(TurnServerConnection* conn, StunMessage* msg);
|
||||
void Send(TurnServerConnection* conn, const rtc::ByteBufferWriter& buf);
|
||||
|
||||
void DestroyAllocation(TurnServerAllocation* allocation) RTC_RUN_ON(thread_);
|
||||
void DestroyInternalSocket(rtc::AsyncPacketSocket* socket)
|
||||
RTC_RUN_ON(thread_);
|
||||
|
||||
typedef std::map<rtc::AsyncPacketSocket*, ProtocolType> InternalSocketMap;
|
||||
struct ServerSocketInfo {
|
||||
ProtocolType proto;
|
||||
// If non-null, used to wrap accepted sockets.
|
||||
std::unique_ptr<rtc::SSLAdapterFactory> ssl_adapter_factory;
|
||||
};
|
||||
typedef std::map<rtc::Socket*, ServerSocketInfo> ServerSocketMap;
|
||||
|
||||
webrtc::TaskQueueBase* const thread_;
|
||||
const std::string nonce_key_;
|
||||
std::string realm_ RTC_GUARDED_BY(thread_);
|
||||
std::string software_ RTC_GUARDED_BY(thread_);
|
||||
TurnAuthInterface* auth_hook_ RTC_GUARDED_BY(thread_);
|
||||
TurnRedirectInterface* redirect_hook_ RTC_GUARDED_BY(thread_);
|
||||
// otu - one-time-use. Server will respond with 438 if it's
|
||||
// sees the same nonce in next transaction.
|
||||
bool enable_otu_nonce_ RTC_GUARDED_BY(thread_);
|
||||
bool reject_private_addresses_ = false;
|
||||
// Check for permission when receiving an external packet.
|
||||
bool enable_permission_checks_ = true;
|
||||
|
||||
InternalSocketMap server_sockets_ RTC_GUARDED_BY(thread_);
|
||||
ServerSocketMap server_listen_sockets_ RTC_GUARDED_BY(thread_);
|
||||
std::unique_ptr<rtc::PacketSocketFactory> external_socket_factory_
|
||||
RTC_GUARDED_BY(thread_);
|
||||
rtc::SocketAddress external_addr_ RTC_GUARDED_BY(thread_);
|
||||
|
||||
AllocationMap allocations_ RTC_GUARDED_BY(thread_);
|
||||
|
||||
// For testing only. If this is non-zero, the next NONCE will be generated
|
||||
// from this value, and it will be reset to 0 after generating the NONCE.
|
||||
int64_t ts_for_next_nonce_ RTC_GUARDED_BY(thread_) = 0;
|
||||
|
||||
// For testing only. Used to observe STUN messages received.
|
||||
std::unique_ptr<StunMessageObserver> stun_message_observer_
|
||||
RTC_GUARDED_BY(thread_);
|
||||
|
||||
friend class TurnServerAllocation;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_TURN_SERVER_H_
|
||||
17
TMessagesProj/jni/voip/webrtc/p2p/base/udp_port.h
Normal file
17
TMessagesProj/jni/voip/webrtc/p2p/base/udp_port.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_UDP_PORT_H_
|
||||
#define P2P_BASE_UDP_PORT_H_
|
||||
|
||||
// StunPort will be handling UDPPort functionality.
|
||||
#include "p2p/base/stun_port.h"
|
||||
|
||||
#endif // P2P_BASE_UDP_PORT_H_
|
||||
|
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
* Copyright 2022 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/base/wrapping_active_ice_controller.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "p2p/base/basic_ice_controller.h"
|
||||
#include "p2p/base/connection.h"
|
||||
#include "p2p/base/ice_agent_interface.h"
|
||||
#include "p2p/base/ice_controller_interface.h"
|
||||
#include "p2p/base/ice_switch_reason.h"
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
||||
namespace {
|
||||
using ::webrtc::SafeTask;
|
||||
using ::webrtc::TimeDelta;
|
||||
} // unnamed namespace
|
||||
|
||||
namespace cricket {
|
||||
|
||||
WrappingActiveIceController::WrappingActiveIceController(
|
||||
IceAgentInterface* ice_agent,
|
||||
std::unique_ptr<IceControllerInterface> wrapped)
|
||||
: network_thread_(rtc::Thread::Current()),
|
||||
wrapped_(std::move(wrapped)),
|
||||
agent_(*ice_agent) {
|
||||
RTC_DCHECK(ice_agent != nullptr);
|
||||
}
|
||||
|
||||
WrappingActiveIceController::WrappingActiveIceController(
|
||||
IceAgentInterface* ice_agent,
|
||||
IceControllerFactoryInterface* wrapped_factory,
|
||||
const IceControllerFactoryArgs& wrapped_factory_args)
|
||||
: network_thread_(rtc::Thread::Current()), agent_(*ice_agent) {
|
||||
RTC_DCHECK(ice_agent != nullptr);
|
||||
if (wrapped_factory) {
|
||||
wrapped_ = wrapped_factory->Create(wrapped_factory_args);
|
||||
} else {
|
||||
wrapped_ = std::make_unique<BasicIceController>(wrapped_factory_args);
|
||||
}
|
||||
}
|
||||
|
||||
WrappingActiveIceController::~WrappingActiveIceController() {}
|
||||
|
||||
void WrappingActiveIceController::SetIceConfig(const IceConfig& config) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
wrapped_->SetIceConfig(config);
|
||||
}
|
||||
|
||||
bool WrappingActiveIceController::GetUseCandidateAttribute(
|
||||
const Connection* connection,
|
||||
NominationMode mode,
|
||||
IceMode remote_ice_mode) const {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return wrapped_->GetUseCandidateAttr(connection, mode, remote_ice_mode);
|
||||
}
|
||||
|
||||
void WrappingActiveIceController::OnConnectionAdded(
|
||||
const Connection* connection) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
wrapped_->AddConnection(connection);
|
||||
}
|
||||
|
||||
void WrappingActiveIceController::OnConnectionPinged(
|
||||
const Connection* connection) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
wrapped_->MarkConnectionPinged(connection);
|
||||
}
|
||||
|
||||
void WrappingActiveIceController::OnConnectionUpdated(
|
||||
const Connection* connection) {
|
||||
RTC_LOG(LS_VERBOSE) << "Connection report for " << connection->ToString();
|
||||
// Do nothing. Native ICE controllers have direct access to Connection, so no
|
||||
// need to update connection state separately.
|
||||
}
|
||||
|
||||
void WrappingActiveIceController::OnConnectionSwitched(
|
||||
const Connection* connection) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
selected_connection_ = connection;
|
||||
wrapped_->SetSelectedConnection(connection);
|
||||
}
|
||||
|
||||
void WrappingActiveIceController::OnConnectionDestroyed(
|
||||
const Connection* connection) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
wrapped_->OnConnectionDestroyed(connection);
|
||||
}
|
||||
|
||||
void WrappingActiveIceController::MaybeStartPinging() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (started_pinging_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wrapped_->HasPingableConnection()) {
|
||||
network_thread_->PostTask(
|
||||
SafeTask(task_safety_.flag(), [this]() { SelectAndPingConnection(); }));
|
||||
agent_.OnStartedPinging();
|
||||
started_pinging_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WrappingActiveIceController::SelectAndPingConnection() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
agent_.UpdateConnectionStates();
|
||||
|
||||
IceControllerInterface::PingResult result =
|
||||
wrapped_->SelectConnectionToPing(agent_.GetLastPingSentMs());
|
||||
HandlePingResult(result);
|
||||
}
|
||||
|
||||
void WrappingActiveIceController::HandlePingResult(
|
||||
IceControllerInterface::PingResult result) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
|
||||
if (result.connection.has_value()) {
|
||||
agent_.SendPingRequest(result.connection.value());
|
||||
}
|
||||
|
||||
network_thread_->PostDelayedTask(
|
||||
SafeTask(task_safety_.flag(), [this]() { SelectAndPingConnection(); }),
|
||||
TimeDelta::Millis(result.recheck_delay_ms));
|
||||
}
|
||||
|
||||
void WrappingActiveIceController::OnSortAndSwitchRequest(
|
||||
IceSwitchReason reason) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (!sort_pending_) {
|
||||
network_thread_->PostTask(SafeTask(task_safety_.flag(), [this, reason]() {
|
||||
SortAndSwitchToBestConnection(reason);
|
||||
}));
|
||||
sort_pending_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WrappingActiveIceController::OnImmediateSortAndSwitchRequest(
|
||||
IceSwitchReason reason) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
SortAndSwitchToBestConnection(reason);
|
||||
}
|
||||
|
||||
void WrappingActiveIceController::SortAndSwitchToBestConnection(
|
||||
IceSwitchReason reason) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
|
||||
// Make sure the connection states are up-to-date since this affects how they
|
||||
// will be sorted.
|
||||
agent_.UpdateConnectionStates();
|
||||
|
||||
// Any changes after this point will require a re-sort.
|
||||
sort_pending_ = false;
|
||||
|
||||
IceControllerInterface::SwitchResult result =
|
||||
wrapped_->SortAndSwitchConnection(reason);
|
||||
HandleSwitchResult(reason, result);
|
||||
UpdateStateOnConnectionsResorted();
|
||||
}
|
||||
|
||||
bool WrappingActiveIceController::OnImmediateSwitchRequest(
|
||||
IceSwitchReason reason,
|
||||
const Connection* selected) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
IceControllerInterface::SwitchResult result =
|
||||
wrapped_->ShouldSwitchConnection(reason, selected);
|
||||
HandleSwitchResult(reason, result);
|
||||
return result.connection.has_value();
|
||||
}
|
||||
|
||||
void WrappingActiveIceController::HandleSwitchResult(
|
||||
IceSwitchReason reason_for_switch,
|
||||
IceControllerInterface::SwitchResult result) {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
if (result.connection.has_value()) {
|
||||
RTC_LOG(LS_INFO) << "Switching selected connection due to: "
|
||||
<< IceSwitchReasonToString(reason_for_switch);
|
||||
agent_.SwitchSelectedConnection(result.connection.value(),
|
||||
reason_for_switch);
|
||||
}
|
||||
|
||||
if (result.recheck_event.has_value()) {
|
||||
// If we do not switch to the connection because it missed the receiving
|
||||
// threshold, the new connection is in a better receiving state than the
|
||||
// currently selected connection. So we need to re-check whether it needs
|
||||
// to be switched at a later time.
|
||||
network_thread_->PostDelayedTask(
|
||||
SafeTask(task_safety_.flag(),
|
||||
[this, recheck_reason = result.recheck_event->reason]() {
|
||||
SortAndSwitchToBestConnection(recheck_reason);
|
||||
}),
|
||||
TimeDelta::Millis(result.recheck_event->recheck_delay_ms));
|
||||
}
|
||||
|
||||
agent_.ForgetLearnedStateForConnections(
|
||||
result.connections_to_forget_state_on);
|
||||
}
|
||||
|
||||
void WrappingActiveIceController::UpdateStateOnConnectionsResorted() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
PruneConnections();
|
||||
|
||||
// Update the internal state of the ICE agentl.
|
||||
agent_.UpdateState();
|
||||
|
||||
// Also possibly start pinging.
|
||||
// We could start pinging if:
|
||||
// * The first connection was created.
|
||||
// * ICE credentials were provided.
|
||||
// * A TCP connection became connected.
|
||||
MaybeStartPinging();
|
||||
}
|
||||
|
||||
void WrappingActiveIceController::PruneConnections() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
|
||||
// The controlled side can prune only if the selected connection has been
|
||||
// nominated because otherwise it may prune the connection that will be
|
||||
// selected by the controlling side.
|
||||
// TODO(honghaiz): This is not enough to prevent a connection from being
|
||||
// pruned too early because with aggressive nomination, the controlling side
|
||||
// will nominate every connection until it becomes writable.
|
||||
if (agent_.GetIceRole() == ICEROLE_CONTROLLING ||
|
||||
(selected_connection_ && selected_connection_->nominated())) {
|
||||
std::vector<const Connection*> connections_to_prune =
|
||||
wrapped_->PruneConnections();
|
||||
agent_.PruneConnections(connections_to_prune);
|
||||
}
|
||||
}
|
||||
|
||||
// Only for unit tests
|
||||
const Connection* WrappingActiveIceController::FindNextPingableConnection() {
|
||||
RTC_DCHECK_RUN_ON(network_thread_);
|
||||
return wrapped_->FindNextPingableConnection();
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2022 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_BASE_WRAPPING_ACTIVE_ICE_CONTROLLER_H_
|
||||
#define P2P_BASE_WRAPPING_ACTIVE_ICE_CONTROLLER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "p2p/base/active_ice_controller_interface.h"
|
||||
#include "p2p/base/connection.h"
|
||||
#include "p2p/base/ice_agent_interface.h"
|
||||
#include "p2p/base/ice_controller_factory_interface.h"
|
||||
#include "p2p/base/ice_controller_interface.h"
|
||||
#include "p2p/base/ice_switch_reason.h"
|
||||
#include "p2p/base/ice_transport_internal.h"
|
||||
#include "p2p/base/transport_description.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// WrappingActiveIceController provides the functionality of a legacy passive
|
||||
// ICE controller but packaged as an active ICE Controller.
|
||||
class WrappingActiveIceController : public ActiveIceControllerInterface {
|
||||
public:
|
||||
// Constructs an active ICE controller wrapping an already constructed legacy
|
||||
// ICE controller. Does not take ownership of the ICE agent, which must
|
||||
// already exist and outlive the ICE controller.
|
||||
WrappingActiveIceController(IceAgentInterface* ice_agent,
|
||||
std::unique_ptr<IceControllerInterface> wrapped);
|
||||
// Constructs an active ICE controller that wraps over a legacy ICE
|
||||
// controller. The legacy ICE controller is constructed through a factory, if
|
||||
// one is supplied. If not, a default BasicIceController is wrapped instead.
|
||||
// Does not take ownership of the ICE agent, which must already exist and
|
||||
// outlive the ICE controller.
|
||||
WrappingActiveIceController(
|
||||
IceAgentInterface* ice_agent,
|
||||
IceControllerFactoryInterface* wrapped_factory,
|
||||
const IceControllerFactoryArgs& wrapped_factory_args);
|
||||
virtual ~WrappingActiveIceController();
|
||||
|
||||
void SetIceConfig(const IceConfig& config) override;
|
||||
bool GetUseCandidateAttribute(const Connection* connection,
|
||||
NominationMode mode,
|
||||
IceMode remote_ice_mode) const override;
|
||||
|
||||
void OnConnectionAdded(const Connection* connection) override;
|
||||
void OnConnectionPinged(const Connection* connection) override;
|
||||
void OnConnectionUpdated(const Connection* connection) override;
|
||||
void OnConnectionSwitched(const Connection* connection) override;
|
||||
void OnConnectionDestroyed(const Connection* connection) override;
|
||||
|
||||
void OnSortAndSwitchRequest(IceSwitchReason reason) override;
|
||||
void OnImmediateSortAndSwitchRequest(IceSwitchReason reason) override;
|
||||
bool OnImmediateSwitchRequest(IceSwitchReason reason,
|
||||
const Connection* selected) override;
|
||||
|
||||
// Only for unit tests
|
||||
const Connection* FindNextPingableConnection() override;
|
||||
|
||||
private:
|
||||
void MaybeStartPinging();
|
||||
void SelectAndPingConnection();
|
||||
void HandlePingResult(IceControllerInterface::PingResult result);
|
||||
|
||||
void SortAndSwitchToBestConnection(IceSwitchReason reason);
|
||||
void HandleSwitchResult(IceSwitchReason reason_for_switch,
|
||||
IceControllerInterface::SwitchResult result);
|
||||
void UpdateStateOnConnectionsResorted();
|
||||
|
||||
void PruneConnections();
|
||||
|
||||
rtc::Thread* const network_thread_;
|
||||
webrtc::ScopedTaskSafety task_safety_;
|
||||
|
||||
bool started_pinging_ RTC_GUARDED_BY(network_thread_) = false;
|
||||
bool sort_pending_ RTC_GUARDED_BY(network_thread_) = false;
|
||||
const Connection* selected_connection_ RTC_GUARDED_BY(network_thread_) =
|
||||
nullptr;
|
||||
|
||||
std::unique_ptr<IceControllerInterface> wrapped_
|
||||
RTC_GUARDED_BY(network_thread_);
|
||||
IceAgentInterface& agent_ RTC_GUARDED_BY(network_thread_);
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_BASE_WRAPPING_ACTIVE_ICE_CONTROLLER_H_
|
||||
1780
TMessagesProj/jni/voip/webrtc/p2p/client/basic_port_allocator.cc
Normal file
1780
TMessagesProj/jni/voip/webrtc/p2p/client/basic_port_allocator.cc
Normal file
File diff suppressed because it is too large
Load diff
415
TMessagesProj/jni/voip/webrtc/p2p/client/basic_port_allocator.h
Normal file
415
TMessagesProj/jni/voip/webrtc/p2p/client/basic_port_allocator.h
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_CLIENT_BASIC_PORT_ALLOCATOR_H_
|
||||
#define P2P_CLIENT_BASIC_PORT_ALLOCATOR_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/field_trials_view.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/turn_customizer.h"
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "p2p/client/relay_port_factory_interface.h"
|
||||
#include "p2p/client/turn_port_factory.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/memory/always_valid_pointer.h"
|
||||
#include "rtc_base/network.h"
|
||||
#include "rtc_base/network/received_packet.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
class RTC_EXPORT BasicPortAllocator : public PortAllocator {
|
||||
public:
|
||||
// The NetworkManager is a mandatory argument. The other arguments are
|
||||
// optional. All pointers are owned by caller and must have a life time
|
||||
// that exceeds that of BasicPortAllocator.
|
||||
BasicPortAllocator(rtc::NetworkManager* network_manager,
|
||||
rtc::PacketSocketFactory* socket_factory,
|
||||
webrtc::TurnCustomizer* customizer = nullptr,
|
||||
RelayPortFactoryInterface* relay_port_factory = nullptr,
|
||||
const webrtc::FieldTrialsView* field_trials = nullptr);
|
||||
BasicPortAllocator(rtc::NetworkManager* network_manager,
|
||||
rtc::PacketSocketFactory* socket_factory,
|
||||
const ServerAddresses& stun_servers,
|
||||
const webrtc::FieldTrialsView* field_trials = nullptr);
|
||||
~BasicPortAllocator() override;
|
||||
|
||||
// Set to kDefaultNetworkIgnoreMask by default.
|
||||
void SetNetworkIgnoreMask(int network_ignore_mask) override;
|
||||
int GetNetworkIgnoreMask() const;
|
||||
|
||||
rtc::NetworkManager* network_manager() const {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return network_manager_;
|
||||
}
|
||||
|
||||
// If socket_factory() is set to NULL each PortAllocatorSession
|
||||
// creates its own socket factory.
|
||||
rtc::PacketSocketFactory* socket_factory() {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return socket_factory_;
|
||||
}
|
||||
|
||||
PortAllocatorSession* CreateSessionInternal(
|
||||
absl::string_view content_name,
|
||||
int component,
|
||||
absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd) override;
|
||||
|
||||
// Convenience method that adds a TURN server to the configuration.
|
||||
void AddTurnServerForTesting(const RelayServerConfig& turn_server);
|
||||
|
||||
RelayPortFactoryInterface* relay_port_factory() {
|
||||
CheckRunOnValidThreadIfInitialized();
|
||||
return relay_port_factory_;
|
||||
}
|
||||
|
||||
void SetVpnList(const std::vector<rtc::NetworkMask>& vpn_list) override;
|
||||
|
||||
const webrtc::FieldTrialsView* field_trials() const {
|
||||
return field_trials_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
bool MdnsObfuscationEnabled() const override;
|
||||
|
||||
webrtc::AlwaysValidPointer<const webrtc::FieldTrialsView,
|
||||
webrtc::FieldTrialBasedConfig>
|
||||
field_trials_;
|
||||
rtc::NetworkManager* network_manager_;
|
||||
// Always externally-owned pointer to a socket factory.
|
||||
rtc::PacketSocketFactory* const socket_factory_;
|
||||
int network_ignore_mask_ = rtc::kDefaultNetworkIgnoreMask;
|
||||
|
||||
// This instance is created if caller does pass a factory.
|
||||
const std::unique_ptr<RelayPortFactoryInterface> default_relay_port_factory_;
|
||||
// This is the factory being used.
|
||||
RelayPortFactoryInterface* const relay_port_factory_;
|
||||
};
|
||||
|
||||
struct PortConfiguration;
|
||||
class AllocationSequence;
|
||||
|
||||
enum class SessionState {
|
||||
GATHERING, // Actively allocating ports and gathering candidates.
|
||||
CLEARED, // Current allocation process has been stopped but may start
|
||||
// new ones.
|
||||
STOPPED // This session has completely stopped, no new allocation
|
||||
// process will be started.
|
||||
};
|
||||
|
||||
// This class is thread-compatible and assumes it's created, operated upon and
|
||||
// destroyed on the network thread.
|
||||
class RTC_EXPORT BasicPortAllocatorSession : public PortAllocatorSession {
|
||||
public:
|
||||
BasicPortAllocatorSession(BasicPortAllocator* allocator,
|
||||
absl::string_view content_name,
|
||||
int component,
|
||||
absl::string_view ice_ufrag,
|
||||
absl::string_view ice_pwd);
|
||||
~BasicPortAllocatorSession() override;
|
||||
|
||||
virtual BasicPortAllocator* allocator();
|
||||
rtc::Thread* network_thread() { return network_thread_; }
|
||||
rtc::PacketSocketFactory* socket_factory() { return socket_factory_; }
|
||||
|
||||
// If the new filter allows new types of candidates compared to the previous
|
||||
// filter, gathered candidates that were discarded because of not matching the
|
||||
// previous filter will be signaled if they match the new one.
|
||||
//
|
||||
// We do not perform any regathering since the port allocator flags decide
|
||||
// the type of candidates to gather and the candidate filter only controls the
|
||||
// signaling of candidates. As a result, with the candidate filter changed
|
||||
// alone, all newly allowed candidates for signaling should already be
|
||||
// gathered by the respective cricket::Port.
|
||||
void SetCandidateFilter(uint32_t filter) override;
|
||||
void StartGettingPorts() override;
|
||||
void StopGettingPorts() override;
|
||||
void ClearGettingPorts() override;
|
||||
bool IsGettingPorts() override;
|
||||
bool IsCleared() const override;
|
||||
bool IsStopped() const override;
|
||||
// These will all be cricket::Ports.
|
||||
std::vector<PortInterface*> ReadyPorts() const override;
|
||||
std::vector<Candidate> ReadyCandidates() const override;
|
||||
bool CandidatesAllocationDone() const override;
|
||||
void RegatherOnFailedNetworks() override;
|
||||
void GetCandidateStatsFromReadyPorts(
|
||||
CandidateStatsList* candidate_stats_list) const override;
|
||||
void SetStunKeepaliveIntervalForReadyPorts(
|
||||
const absl::optional<int>& stun_keepalive_interval) override;
|
||||
void PruneAllPorts() override;
|
||||
static std::vector<const rtc::Network*> SelectIPv6Networks(
|
||||
std::vector<const rtc::Network*>& all_ipv6_networks,
|
||||
int max_ipv6_networks);
|
||||
|
||||
protected:
|
||||
void UpdateIceParametersInternal() override;
|
||||
|
||||
// Starts the process of getting the port configurations.
|
||||
virtual void GetPortConfigurations();
|
||||
|
||||
// Adds a port configuration that is now ready. Once we have one for each
|
||||
// network (or a timeout occurs), we will start allocating ports.
|
||||
void ConfigReady(std::unique_ptr<PortConfiguration> config);
|
||||
// TODO(bugs.webrtc.org/12840) Remove once unused in downstream projects.
|
||||
ABSL_DEPRECATED(
|
||||
"Use ConfigReady(std::unique_ptr<PortConfiguration>) instead!")
|
||||
void ConfigReady(PortConfiguration* config);
|
||||
|
||||
private:
|
||||
class PortData {
|
||||
public:
|
||||
enum State {
|
||||
STATE_INPROGRESS, // Still gathering candidates.
|
||||
STATE_COMPLETE, // All candidates allocated and ready for process.
|
||||
STATE_ERROR, // Error in gathering candidates.
|
||||
STATE_PRUNED // Pruned by higher priority ports on the same network
|
||||
// interface. Only TURN ports may be pruned.
|
||||
};
|
||||
|
||||
PortData() {}
|
||||
PortData(Port* port, AllocationSequence* seq)
|
||||
: port_(port), sequence_(seq) {}
|
||||
|
||||
Port* port() const { return port_; }
|
||||
AllocationSequence* sequence() const { return sequence_; }
|
||||
bool has_pairable_candidate() const { return has_pairable_candidate_; }
|
||||
State state() const { return state_; }
|
||||
bool complete() const { return state_ == STATE_COMPLETE; }
|
||||
bool error() const { return state_ == STATE_ERROR; }
|
||||
bool pruned() const { return state_ == STATE_PRUNED; }
|
||||
bool inprogress() const { return state_ == STATE_INPROGRESS; }
|
||||
// Returns true if this port is ready to be used.
|
||||
bool ready() const {
|
||||
return has_pairable_candidate_ && state_ != STATE_ERROR &&
|
||||
state_ != STATE_PRUNED;
|
||||
}
|
||||
// Sets the state to "PRUNED" and prunes the Port.
|
||||
void Prune() {
|
||||
state_ = STATE_PRUNED;
|
||||
if (port()) {
|
||||
port()->Prune();
|
||||
}
|
||||
}
|
||||
void set_has_pairable_candidate(bool has_pairable_candidate) {
|
||||
if (has_pairable_candidate) {
|
||||
RTC_DCHECK(state_ == STATE_INPROGRESS);
|
||||
}
|
||||
has_pairable_candidate_ = has_pairable_candidate;
|
||||
}
|
||||
void set_state(State state) {
|
||||
RTC_DCHECK(state != STATE_ERROR || state_ == STATE_INPROGRESS);
|
||||
state_ = state;
|
||||
}
|
||||
|
||||
private:
|
||||
Port* port_ = nullptr;
|
||||
AllocationSequence* sequence_ = nullptr;
|
||||
bool has_pairable_candidate_ = false;
|
||||
State state_ = STATE_INPROGRESS;
|
||||
};
|
||||
|
||||
void OnConfigReady(std::unique_ptr<PortConfiguration> config);
|
||||
void OnConfigStop();
|
||||
void AllocatePorts();
|
||||
void OnAllocate(int allocation_epoch);
|
||||
void DoAllocate(bool disable_equivalent_phases);
|
||||
void OnNetworksChanged();
|
||||
void OnAllocationSequenceObjectsCreated();
|
||||
void DisableEquivalentPhases(const rtc::Network* network,
|
||||
PortConfiguration* config,
|
||||
uint32_t* flags);
|
||||
void AddAllocatedPort(Port* port, AllocationSequence* seq);
|
||||
void OnCandidateReady(Port* port, const Candidate& c);
|
||||
void OnCandidateError(Port* port, const IceCandidateErrorEvent& event);
|
||||
void OnPortComplete(Port* port);
|
||||
void OnPortError(Port* port);
|
||||
void OnProtocolEnabled(AllocationSequence* seq, ProtocolType proto);
|
||||
void OnPortDestroyed(PortInterface* port);
|
||||
void MaybeSignalCandidatesAllocationDone();
|
||||
void OnPortAllocationComplete();
|
||||
PortData* FindPort(Port* port);
|
||||
std::vector<const rtc::Network*> GetNetworks();
|
||||
std::vector<const rtc::Network*> GetFailedNetworks();
|
||||
void Regather(const std::vector<const rtc::Network*>& networks,
|
||||
bool disable_equivalent_phases,
|
||||
IceRegatheringReason reason);
|
||||
|
||||
bool CheckCandidateFilter(const Candidate& c) const;
|
||||
bool CandidatePairable(const Candidate& c, const Port* port) const;
|
||||
|
||||
std::vector<PortData*> GetUnprunedPorts(
|
||||
const std::vector<const rtc::Network*>& networks);
|
||||
// Prunes ports and signal the remote side to remove the candidates that
|
||||
// were previously signaled from these ports.
|
||||
void PrunePortsAndRemoveCandidates(
|
||||
const std::vector<PortData*>& port_data_list);
|
||||
// Gets filtered and sanitized candidates generated from a port and
|
||||
// append to `candidates`.
|
||||
void GetCandidatesFromPort(const PortData& data,
|
||||
std::vector<Candidate>* candidates) const;
|
||||
Port* GetBestTurnPortForNetwork(absl::string_view network_name) const;
|
||||
// Returns true if at least one TURN port is pruned.
|
||||
bool PruneTurnPorts(Port* newly_pairable_turn_port);
|
||||
bool PruneNewlyPairableTurnPort(PortData* newly_pairable_turn_port);
|
||||
|
||||
BasicPortAllocator* allocator_;
|
||||
rtc::Thread* network_thread_;
|
||||
rtc::PacketSocketFactory* socket_factory_;
|
||||
bool allocation_started_;
|
||||
bool network_manager_started_;
|
||||
bool allocation_sequences_created_;
|
||||
std::vector<std::unique_ptr<PortConfiguration>> configs_;
|
||||
std::vector<AllocationSequence*> sequences_;
|
||||
std::vector<PortData> ports_;
|
||||
std::vector<IceCandidateErrorEvent> candidate_error_events_;
|
||||
uint32_t candidate_filter_ = CF_ALL;
|
||||
// Policy on how to prune turn ports, taken from the port allocator.
|
||||
webrtc::PortPrunePolicy turn_port_prune_policy_;
|
||||
SessionState state_ = SessionState::CLEARED;
|
||||
int allocation_epoch_ RTC_GUARDED_BY(network_thread_) = 0;
|
||||
webrtc::ScopedTaskSafety network_safety_;
|
||||
|
||||
friend class AllocationSequence;
|
||||
};
|
||||
|
||||
// Records configuration information useful in creating ports.
|
||||
// TODO(deadbeef): Rename "relay" to "turn_server" in this struct.
|
||||
struct RTC_EXPORT PortConfiguration {
|
||||
// TODO(jiayl): remove `stun_address` when Chrome is updated.
|
||||
rtc::SocketAddress stun_address;
|
||||
ServerAddresses stun_servers;
|
||||
std::string username;
|
||||
std::string password;
|
||||
bool use_turn_server_as_stun_server_disabled = false;
|
||||
|
||||
typedef std::vector<RelayServerConfig> RelayList;
|
||||
RelayList relays;
|
||||
|
||||
PortConfiguration(const ServerAddresses& stun_servers,
|
||||
absl::string_view username,
|
||||
absl::string_view password,
|
||||
const webrtc::FieldTrialsView* field_trials = nullptr);
|
||||
|
||||
// Returns addresses of both the explicitly configured STUN servers,
|
||||
// and TURN servers that should be used as STUN servers.
|
||||
ServerAddresses StunServers();
|
||||
|
||||
// Adds another relay server, with the given ports and modifier, to the list.
|
||||
void AddRelay(const RelayServerConfig& config);
|
||||
|
||||
// Determines whether the given relay server supports the given protocol.
|
||||
bool SupportsProtocol(const RelayServerConfig& relay,
|
||||
ProtocolType type) const;
|
||||
bool SupportsProtocol(ProtocolType type) const;
|
||||
// Helper method returns the server addresses for the matching RelayType and
|
||||
// Protocol type.
|
||||
ServerAddresses GetRelayServerAddresses(ProtocolType type) const;
|
||||
};
|
||||
|
||||
class UDPPort;
|
||||
class TurnPort;
|
||||
|
||||
// Performs the allocation of ports, in a sequenced (timed) manner, for a given
|
||||
// network and IP address.
|
||||
// This class is thread-compatible.
|
||||
class AllocationSequence {
|
||||
public:
|
||||
enum State {
|
||||
kInit, // Initial state.
|
||||
kRunning, // Started allocating ports.
|
||||
kStopped, // Stopped from running.
|
||||
kCompleted, // All ports are allocated.
|
||||
|
||||
// kInit --> kRunning --> {kCompleted|kStopped}
|
||||
};
|
||||
// `port_allocation_complete_callback` is called when AllocationSequence is
|
||||
// done with allocating ports. This signal is useful when port allocation
|
||||
// fails which doesn't result in any candidates. Using this signal
|
||||
// BasicPortAllocatorSession can send its candidate discovery conclusion
|
||||
// signal. Without this signal, BasicPortAllocatorSession doesn't have any
|
||||
// event to trigger signal. This can also be achieved by starting a timer in
|
||||
// BPAS, but this is less deterministic.
|
||||
AllocationSequence(BasicPortAllocatorSession* session,
|
||||
const rtc::Network* network,
|
||||
PortConfiguration* config,
|
||||
uint32_t flags,
|
||||
std::function<void()> port_allocation_complete_callback);
|
||||
void Init();
|
||||
void Clear();
|
||||
void OnNetworkFailed();
|
||||
|
||||
State state() const { return state_; }
|
||||
const rtc::Network* network() const { return network_; }
|
||||
|
||||
bool network_failed() const { return network_failed_; }
|
||||
void set_network_failed() { network_failed_ = true; }
|
||||
|
||||
// Disables the phases for a new sequence that this one already covers for an
|
||||
// equivalent network setup.
|
||||
void DisableEquivalentPhases(const rtc::Network* network,
|
||||
PortConfiguration* config,
|
||||
uint32_t* flags);
|
||||
|
||||
// Starts and stops the sequence. When started, it will continue allocating
|
||||
// new ports on its own timed schedule.
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
void CreateTurnPort(const RelayServerConfig& config, int relative_priority);
|
||||
|
||||
typedef std::vector<ProtocolType> ProtocolList;
|
||||
|
||||
void Process(int epoch);
|
||||
bool IsFlagSet(uint32_t flag) { return ((flags_ & flag) != 0); }
|
||||
void CreateUDPPorts();
|
||||
void CreateTCPPorts();
|
||||
void CreateStunPorts();
|
||||
void CreateRelayPorts();
|
||||
|
||||
void OnReadPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet);
|
||||
|
||||
void OnPortDestroyed(PortInterface* port);
|
||||
|
||||
BasicPortAllocatorSession* session_;
|
||||
bool network_failed_ = false;
|
||||
const rtc::Network* network_;
|
||||
// Compared with the new best IP in DisableEquivalentPhases.
|
||||
rtc::IPAddress previous_best_ip_;
|
||||
PortConfiguration* config_;
|
||||
State state_;
|
||||
uint32_t flags_;
|
||||
ProtocolList protocols_;
|
||||
std::unique_ptr<rtc::AsyncPacketSocket> udp_socket_;
|
||||
// There will be only one udp port per AllocationSequence.
|
||||
UDPPort* udp_port_;
|
||||
std::vector<Port*> relay_ports_;
|
||||
int phase_;
|
||||
std::function<void()> port_allocation_complete_callback_;
|
||||
// This counter is sampled and passed together with tasks when tasks are
|
||||
// posted. If the sampled counter doesn't match `epoch_` on reception, the
|
||||
// posted task is ignored.
|
||||
int epoch_ = 0;
|
||||
webrtc::ScopedTaskSafety safety_;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_CLIENT_BASIC_PORT_ALLOCATOR_H_
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2017 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_CLIENT_RELAY_PORT_FACTORY_INTERFACE_H_
|
||||
#define P2P_CLIENT_RELAY_PORT_FACTORY_INTERFACE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "p2p/base/port_interface.h"
|
||||
#include "rtc_base/ref_count.h"
|
||||
|
||||
namespace rtc {
|
||||
class AsyncPacketSocket;
|
||||
class Network;
|
||||
class PacketSocketFactory;
|
||||
class Thread;
|
||||
} // namespace rtc
|
||||
|
||||
namespace webrtc {
|
||||
class TurnCustomizer;
|
||||
class FieldTrialsView;
|
||||
} // namespace webrtc
|
||||
|
||||
namespace cricket {
|
||||
class Port;
|
||||
struct ProtocolAddress;
|
||||
struct RelayServerConfig;
|
||||
|
||||
// A struct containing arguments to RelayPortFactory::Create()
|
||||
struct CreateRelayPortArgs {
|
||||
rtc::Thread* network_thread;
|
||||
rtc::PacketSocketFactory* socket_factory;
|
||||
const rtc::Network* network;
|
||||
const ProtocolAddress* server_address;
|
||||
const RelayServerConfig* config;
|
||||
std::string username;
|
||||
std::string password;
|
||||
webrtc::TurnCustomizer* turn_customizer = nullptr;
|
||||
const webrtc::FieldTrialsView* field_trials = nullptr;
|
||||
// Relative priority of candidates from this TURN server in relation
|
||||
// to the candidates from other servers. Required because ICE priorities
|
||||
// need to be unique.
|
||||
int relative_priority = 0;
|
||||
};
|
||||
|
||||
// A factory for creating RelayPort's.
|
||||
class RelayPortFactoryInterface {
|
||||
public:
|
||||
virtual ~RelayPortFactoryInterface() {}
|
||||
|
||||
// This variant is used for UDP connection to the relay server
|
||||
// using a already existing shared socket.
|
||||
virtual std::unique_ptr<Port> Create(const CreateRelayPortArgs& args,
|
||||
rtc::AsyncPacketSocket* udp_socket) = 0;
|
||||
|
||||
// This variant is used for the other cases.
|
||||
virtual std::unique_ptr<Port> Create(const CreateRelayPortArgs& args,
|
||||
int min_port,
|
||||
int max_port) = 0;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_CLIENT_RELAY_PORT_FACTORY_INTERFACE_H_
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2017 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/client/turn_port_factory.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "p2p/base/port_allocator.h"
|
||||
#include "p2p/base/turn_port.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
TurnPortFactory::~TurnPortFactory() {}
|
||||
|
||||
std::unique_ptr<Port> TurnPortFactory::Create(
|
||||
const CreateRelayPortArgs& args,
|
||||
rtc::AsyncPacketSocket* udp_socket) {
|
||||
auto port = TurnPort::Create(args, udp_socket);
|
||||
if (!port)
|
||||
return nullptr;
|
||||
port->SetTlsCertPolicy(args.config->tls_cert_policy);
|
||||
port->SetTurnLoggingId(args.config->turn_logging_id);
|
||||
return std::move(port);
|
||||
}
|
||||
|
||||
std::unique_ptr<Port> TurnPortFactory::Create(const CreateRelayPortArgs& args,
|
||||
int min_port,
|
||||
int max_port) {
|
||||
auto port = TurnPort::Create(args, min_port, max_port);
|
||||
if (!port)
|
||||
return nullptr;
|
||||
port->SetTlsCertPolicy(args.config->tls_cert_policy);
|
||||
port->SetTurnLoggingId(args.config->turn_logging_id);
|
||||
return std::move(port);
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
37
TMessagesProj/jni/voip/webrtc/p2p/client/turn_port_factory.h
Normal file
37
TMessagesProj/jni/voip/webrtc/p2p/client/turn_port_factory.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2017 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_CLIENT_TURN_PORT_FACTORY_H_
|
||||
#define P2P_CLIENT_TURN_PORT_FACTORY_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "p2p/base/port.h"
|
||||
#include "p2p/client/relay_port_factory_interface.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// This is a RelayPortFactory that produces TurnPorts.
|
||||
class TurnPortFactory : public RelayPortFactoryInterface {
|
||||
public:
|
||||
~TurnPortFactory() override;
|
||||
|
||||
std::unique_ptr<Port> Create(const CreateRelayPortArgs& args,
|
||||
rtc::AsyncPacketSocket* udp_socket) override;
|
||||
|
||||
std::unique_ptr<Port> Create(const CreateRelayPortArgs& args,
|
||||
int min_port,
|
||||
int max_port) override;
|
||||
};
|
||||
|
||||
} // namespace cricket
|
||||
|
||||
#endif // P2P_CLIENT_TURN_PORT_FACTORY_H_
|
||||
602
TMessagesProj/jni/voip/webrtc/p2p/stunprober/stun_prober.cc
Normal file
602
TMessagesProj/jni/voip/webrtc/p2p/stunprober/stun_prober.cc
Normal file
|
|
@ -0,0 +1,602 @@
|
|||
/*
|
||||
* Copyright 2015 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "p2p/stunprober/stun_prober.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "api/packet_socket_factory.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "api/transport/stun.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/network/received_packet.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
||||
namespace stunprober {
|
||||
|
||||
namespace {
|
||||
using ::webrtc::SafeTask;
|
||||
using ::webrtc::TimeDelta;
|
||||
|
||||
const int THREAD_WAKE_UP_INTERVAL_MS = 5;
|
||||
|
||||
template <typename T>
|
||||
void IncrementCounterByAddress(std::map<T, int>* counter_per_ip, const T& ip) {
|
||||
counter_per_ip->insert(std::make_pair(ip, 0)).first->second++;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// A requester tracks the requests and responses from a single socket to many
|
||||
// STUN servers
|
||||
class StunProber::Requester : public sigslot::has_slots<> {
|
||||
public:
|
||||
// Each Request maps to a request and response.
|
||||
struct Request {
|
||||
// Actual time the STUN bind request was sent.
|
||||
int64_t sent_time_ms = 0;
|
||||
// Time the response was received.
|
||||
int64_t received_time_ms = 0;
|
||||
|
||||
// Server reflexive address from STUN response for this given request.
|
||||
rtc::SocketAddress srflx_addr;
|
||||
|
||||
rtc::IPAddress server_addr;
|
||||
|
||||
int64_t rtt() { return received_time_ms - sent_time_ms; }
|
||||
void ProcessResponse(rtc::ArrayView<const uint8_t> payload);
|
||||
};
|
||||
|
||||
// StunProber provides `server_ips` for Requester to probe. For shared
|
||||
// socket mode, it'll be all the resolved IP addresses. For non-shared mode,
|
||||
// it'll just be a single address.
|
||||
Requester(StunProber* prober,
|
||||
rtc::AsyncPacketSocket* socket,
|
||||
const std::vector<rtc::SocketAddress>& server_ips);
|
||||
~Requester() override;
|
||||
|
||||
Requester(const Requester&) = delete;
|
||||
Requester& operator=(const Requester&) = delete;
|
||||
|
||||
// There is no callback for SendStunRequest as the underneath socket send is
|
||||
// expected to be completed immediately. Otherwise, it'll skip this request
|
||||
// and move to the next one.
|
||||
void SendStunRequest();
|
||||
|
||||
void OnStunResponseReceived(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet);
|
||||
|
||||
const std::vector<Request*>& requests() { return requests_; }
|
||||
|
||||
// Whether this Requester has completed all requests.
|
||||
bool Done() {
|
||||
return static_cast<size_t>(num_request_sent_) == server_ips_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
Request* GetRequestByAddress(const rtc::IPAddress& ip);
|
||||
|
||||
StunProber* prober_;
|
||||
|
||||
// The socket for this session.
|
||||
std::unique_ptr<rtc::AsyncPacketSocket> socket_;
|
||||
|
||||
// Temporary SocketAddress and buffer for RecvFrom.
|
||||
rtc::SocketAddress addr_;
|
||||
std::unique_ptr<rtc::ByteBufferWriter> response_packet_;
|
||||
|
||||
std::vector<Request*> requests_;
|
||||
std::vector<rtc::SocketAddress> server_ips_;
|
||||
int16_t num_request_sent_ = 0;
|
||||
int16_t num_response_received_ = 0;
|
||||
|
||||
webrtc::SequenceChecker& thread_checker_;
|
||||
};
|
||||
|
||||
StunProber::Requester::Requester(
|
||||
StunProber* prober,
|
||||
rtc::AsyncPacketSocket* socket,
|
||||
const std::vector<rtc::SocketAddress>& server_ips)
|
||||
: prober_(prober),
|
||||
socket_(socket),
|
||||
response_packet_(new rtc::ByteBufferWriter(nullptr, kMaxUdpBufferSize)),
|
||||
server_ips_(server_ips),
|
||||
thread_checker_(prober->thread_checker_) {
|
||||
socket_->RegisterReceivedPacketCallback(
|
||||
[&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) {
|
||||
OnStunResponseReceived(socket, packet);
|
||||
});
|
||||
}
|
||||
|
||||
StunProber::Requester::~Requester() {
|
||||
if (socket_) {
|
||||
socket_->Close();
|
||||
}
|
||||
for (auto* req : requests_) {
|
||||
if (req) {
|
||||
delete req;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StunProber::Requester::SendStunRequest() {
|
||||
RTC_DCHECK(thread_checker_.IsCurrent());
|
||||
requests_.push_back(new Request());
|
||||
Request& request = *(requests_.back());
|
||||
// Random transaction ID, STUN_BINDING_REQUEST
|
||||
cricket::StunMessage message(cricket::STUN_BINDING_REQUEST);
|
||||
|
||||
std::unique_ptr<rtc::ByteBufferWriter> request_packet(
|
||||
new rtc::ByteBufferWriter(nullptr, kMaxUdpBufferSize));
|
||||
if (!message.Write(request_packet.get())) {
|
||||
prober_->ReportOnFinished(WRITE_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
auto addr = server_ips_[num_request_sent_];
|
||||
request.server_addr = addr.ipaddr();
|
||||
|
||||
// The write must succeed immediately. Otherwise, the calculating of the STUN
|
||||
// request timing could become too complicated. Callback is ignored by passing
|
||||
// empty AsyncCallback.
|
||||
rtc::PacketOptions options;
|
||||
int rv = socket_->SendTo(request_packet->Data(), request_packet->Length(),
|
||||
addr, options);
|
||||
if (rv < 0) {
|
||||
prober_->ReportOnFinished(WRITE_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
request.sent_time_ms = rtc::TimeMillis();
|
||||
|
||||
num_request_sent_++;
|
||||
RTC_DCHECK(static_cast<size_t>(num_request_sent_) <= server_ips_.size());
|
||||
}
|
||||
|
||||
void StunProber::Requester::Request::ProcessResponse(
|
||||
rtc::ArrayView<const uint8_t> payload) {
|
||||
int64_t now = rtc::TimeMillis();
|
||||
rtc::ByteBufferReader message(payload);
|
||||
cricket::StunMessage stun_response;
|
||||
if (!stun_response.Read(&message)) {
|
||||
// Invalid or incomplete STUN packet.
|
||||
received_time_ms = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get external address of the socket.
|
||||
const cricket::StunAddressAttribute* addr_attr =
|
||||
stun_response.GetAddress(cricket::STUN_ATTR_MAPPED_ADDRESS);
|
||||
if (addr_attr == nullptr) {
|
||||
// Addresses not available to detect whether or not behind a NAT.
|
||||
return;
|
||||
}
|
||||
|
||||
if (addr_attr->family() != cricket::STUN_ADDRESS_IPV4 &&
|
||||
addr_attr->family() != cricket::STUN_ADDRESS_IPV6) {
|
||||
return;
|
||||
}
|
||||
|
||||
received_time_ms = now;
|
||||
|
||||
srflx_addr = addr_attr->GetAddress();
|
||||
}
|
||||
|
||||
void StunProber::Requester::OnStunResponseReceived(
|
||||
rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet) {
|
||||
RTC_DCHECK(thread_checker_.IsCurrent());
|
||||
RTC_DCHECK(socket_);
|
||||
Request* request = GetRequestByAddress(packet.source_address().ipaddr());
|
||||
if (!request) {
|
||||
// Something is wrong, finish the test.
|
||||
prober_->ReportOnFinished(GENERIC_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
num_response_received_++;
|
||||
request->ProcessResponse(packet.payload());
|
||||
}
|
||||
|
||||
StunProber::Requester::Request* StunProber::Requester::GetRequestByAddress(
|
||||
const rtc::IPAddress& ipaddr) {
|
||||
RTC_DCHECK(thread_checker_.IsCurrent());
|
||||
for (auto* request : requests_) {
|
||||
if (request->server_addr == ipaddr) {
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
StunProber::Stats::Stats() = default;
|
||||
|
||||
StunProber::Stats::~Stats() = default;
|
||||
|
||||
StunProber::ObserverAdapter::ObserverAdapter() = default;
|
||||
|
||||
StunProber::ObserverAdapter::~ObserverAdapter() = default;
|
||||
|
||||
void StunProber::ObserverAdapter::OnPrepared(StunProber* stunprober,
|
||||
Status status) {
|
||||
if (status == SUCCESS) {
|
||||
stunprober->Start(this);
|
||||
} else {
|
||||
callback_(stunprober, status);
|
||||
}
|
||||
}
|
||||
|
||||
void StunProber::ObserverAdapter::OnFinished(StunProber* stunprober,
|
||||
Status status) {
|
||||
callback_(stunprober, status);
|
||||
}
|
||||
|
||||
StunProber::StunProber(rtc::PacketSocketFactory* socket_factory,
|
||||
rtc::Thread* thread,
|
||||
std::vector<const rtc::Network*> networks)
|
||||
: interval_ms_(0),
|
||||
socket_factory_(socket_factory),
|
||||
thread_(thread),
|
||||
networks_(std::move(networks)) {}
|
||||
|
||||
StunProber::~StunProber() {
|
||||
RTC_DCHECK(thread_checker_.IsCurrent());
|
||||
for (auto* req : requesters_) {
|
||||
if (req) {
|
||||
delete req;
|
||||
}
|
||||
}
|
||||
for (auto* s : sockets_) {
|
||||
if (s) {
|
||||
delete s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool StunProber::Start(const std::vector<rtc::SocketAddress>& servers,
|
||||
bool shared_socket_mode,
|
||||
int interval_ms,
|
||||
int num_request_per_ip,
|
||||
int timeout_ms,
|
||||
const AsyncCallback callback) {
|
||||
observer_adapter_.set_callback(callback);
|
||||
return Prepare(servers, shared_socket_mode, interval_ms, num_request_per_ip,
|
||||
timeout_ms, &observer_adapter_);
|
||||
}
|
||||
|
||||
bool StunProber::Prepare(const std::vector<rtc::SocketAddress>& servers,
|
||||
bool shared_socket_mode,
|
||||
int interval_ms,
|
||||
int num_request_per_ip,
|
||||
int timeout_ms,
|
||||
StunProber::Observer* observer) {
|
||||
RTC_DCHECK(thread_checker_.IsCurrent());
|
||||
interval_ms_ = interval_ms;
|
||||
shared_socket_mode_ = shared_socket_mode;
|
||||
|
||||
requests_per_ip_ = num_request_per_ip;
|
||||
if (requests_per_ip_ == 0 || servers.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
timeout_ms_ = timeout_ms;
|
||||
servers_ = servers;
|
||||
observer_ = observer;
|
||||
// Remove addresses that are already resolved.
|
||||
for (auto it = servers_.begin(); it != servers_.end();) {
|
||||
if (it->ipaddr().family() != AF_UNSPEC) {
|
||||
all_servers_addrs_.push_back(*it);
|
||||
it = servers_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (servers_.empty()) {
|
||||
CreateSockets();
|
||||
return true;
|
||||
}
|
||||
return ResolveServerName(servers_.back());
|
||||
}
|
||||
|
||||
bool StunProber::Start(StunProber::Observer* observer) {
|
||||
observer_ = observer;
|
||||
if (total_ready_sockets_ != total_socket_required()) {
|
||||
return false;
|
||||
}
|
||||
MaybeScheduleStunRequests();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StunProber::ResolveServerName(const rtc::SocketAddress& addr) {
|
||||
RTC_DCHECK(!resolver_);
|
||||
resolver_ = socket_factory_->CreateAsyncDnsResolver();
|
||||
if (!resolver_) {
|
||||
return false;
|
||||
}
|
||||
resolver_->Start(addr, [this] { OnServerResolved(resolver_->result()); });
|
||||
return true;
|
||||
}
|
||||
|
||||
void StunProber::OnSocketReady(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::SocketAddress& addr) {
|
||||
total_ready_sockets_++;
|
||||
if (total_ready_sockets_ == total_socket_required()) {
|
||||
ReportOnPrepared(SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
void StunProber::OnServerResolved(
|
||||
const webrtc::AsyncDnsResolverResult& result) {
|
||||
RTC_DCHECK(thread_checker_.IsCurrent());
|
||||
rtc::SocketAddress received_address;
|
||||
if (result.GetResolvedAddress(AF_INET, &received_address)) {
|
||||
// Construct an address without the name in it.
|
||||
rtc::SocketAddress addr(received_address.ipaddr(), received_address.port());
|
||||
all_servers_addrs_.push_back(addr);
|
||||
}
|
||||
resolver_.reset();
|
||||
servers_.pop_back();
|
||||
if (servers_.size()) {
|
||||
if (!ResolveServerName(servers_.back())) {
|
||||
ReportOnPrepared(RESOLVE_FAILED);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (all_servers_addrs_.size() == 0) {
|
||||
ReportOnPrepared(RESOLVE_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
CreateSockets();
|
||||
}
|
||||
|
||||
void StunProber::CreateSockets() {
|
||||
// Dedupe.
|
||||
std::set<rtc::SocketAddress> addrs(all_servers_addrs_.begin(),
|
||||
all_servers_addrs_.end());
|
||||
all_servers_addrs_.assign(addrs.begin(), addrs.end());
|
||||
|
||||
// Prepare all the sockets beforehand. All of them will bind to "any" address.
|
||||
while (sockets_.size() < total_socket_required()) {
|
||||
std::unique_ptr<rtc::AsyncPacketSocket> socket(
|
||||
socket_factory_->CreateUdpSocket(rtc::SocketAddress(INADDR_ANY, 0), 0,
|
||||
0));
|
||||
if (!socket) {
|
||||
ReportOnPrepared(GENERIC_FAILURE);
|
||||
return;
|
||||
}
|
||||
// Chrome and WebRTC behave differently in terms of the state of a socket
|
||||
// once returned from PacketSocketFactory::CreateUdpSocket.
|
||||
if (socket->GetState() == rtc::AsyncPacketSocket::STATE_BINDING) {
|
||||
socket->SignalAddressReady.connect(this, &StunProber::OnSocketReady);
|
||||
} else {
|
||||
OnSocketReady(socket.get(), rtc::SocketAddress(INADDR_ANY, 0));
|
||||
}
|
||||
sockets_.push_back(socket.release());
|
||||
}
|
||||
}
|
||||
|
||||
StunProber::Requester* StunProber::CreateRequester() {
|
||||
RTC_DCHECK(thread_checker_.IsCurrent());
|
||||
if (!sockets_.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
StunProber::Requester* requester;
|
||||
if (shared_socket_mode_) {
|
||||
requester = new Requester(this, sockets_.back(), all_servers_addrs_);
|
||||
} else {
|
||||
std::vector<rtc::SocketAddress> server_ip;
|
||||
server_ip.push_back(
|
||||
all_servers_addrs_[(num_request_sent_ % all_servers_addrs_.size())]);
|
||||
requester = new Requester(this, sockets_.back(), server_ip);
|
||||
}
|
||||
|
||||
sockets_.pop_back();
|
||||
return requester;
|
||||
}
|
||||
|
||||
bool StunProber::SendNextRequest() {
|
||||
if (!current_requester_ || current_requester_->Done()) {
|
||||
current_requester_ = CreateRequester();
|
||||
requesters_.push_back(current_requester_);
|
||||
}
|
||||
if (!current_requester_) {
|
||||
return false;
|
||||
}
|
||||
current_requester_->SendStunRequest();
|
||||
num_request_sent_++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StunProber::should_send_next_request(int64_t now) {
|
||||
if (interval_ms_ < THREAD_WAKE_UP_INTERVAL_MS) {
|
||||
return now >= next_request_time_ms_;
|
||||
} else {
|
||||
return (now + (THREAD_WAKE_UP_INTERVAL_MS / 2)) >= next_request_time_ms_;
|
||||
}
|
||||
}
|
||||
|
||||
int StunProber::get_wake_up_interval_ms() {
|
||||
if (interval_ms_ < THREAD_WAKE_UP_INTERVAL_MS) {
|
||||
return 1;
|
||||
} else {
|
||||
return THREAD_WAKE_UP_INTERVAL_MS;
|
||||
}
|
||||
}
|
||||
|
||||
void StunProber::MaybeScheduleStunRequests() {
|
||||
RTC_DCHECK_RUN_ON(thread_);
|
||||
int64_t now = rtc::TimeMillis();
|
||||
|
||||
if (Done()) {
|
||||
thread_->PostDelayedTask(
|
||||
SafeTask(task_safety_.flag(), [this] { ReportOnFinished(SUCCESS); }),
|
||||
TimeDelta::Millis(timeout_ms_));
|
||||
return;
|
||||
}
|
||||
if (should_send_next_request(now)) {
|
||||
if (!SendNextRequest()) {
|
||||
ReportOnFinished(GENERIC_FAILURE);
|
||||
return;
|
||||
}
|
||||
next_request_time_ms_ = now + interval_ms_;
|
||||
}
|
||||
thread_->PostDelayedTask(
|
||||
SafeTask(task_safety_.flag(), [this] { MaybeScheduleStunRequests(); }),
|
||||
TimeDelta::Millis(get_wake_up_interval_ms()));
|
||||
}
|
||||
|
||||
bool StunProber::GetStats(StunProber::Stats* prob_stats) const {
|
||||
// No need to be on the same thread.
|
||||
if (!prob_stats) {
|
||||
return false;
|
||||
}
|
||||
|
||||
StunProber::Stats stats;
|
||||
|
||||
int rtt_sum = 0;
|
||||
int64_t first_sent_time = 0;
|
||||
int64_t last_sent_time = 0;
|
||||
NatType nat_type = NATTYPE_INVALID;
|
||||
|
||||
// Track of how many srflx IP that we have seen.
|
||||
std::set<rtc::IPAddress> srflx_ips;
|
||||
|
||||
// If we're not receiving any response on a given IP, all requests sent to
|
||||
// that IP should be ignored as this could just be an DNS error.
|
||||
std::map<rtc::IPAddress, int> num_response_per_server;
|
||||
std::map<rtc::IPAddress, int> num_request_per_server;
|
||||
|
||||
for (auto* requester : requesters_) {
|
||||
std::map<rtc::SocketAddress, int> num_response_per_srflx_addr;
|
||||
for (auto* request : requester->requests()) {
|
||||
if (request->sent_time_ms <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
++stats.raw_num_request_sent;
|
||||
IncrementCounterByAddress(&num_request_per_server, request->server_addr);
|
||||
|
||||
if (!first_sent_time) {
|
||||
first_sent_time = request->sent_time_ms;
|
||||
}
|
||||
last_sent_time = request->sent_time_ms;
|
||||
|
||||
if (request->received_time_ms < request->sent_time_ms) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IncrementCounterByAddress(&num_response_per_server, request->server_addr);
|
||||
IncrementCounterByAddress(&num_response_per_srflx_addr,
|
||||
request->srflx_addr);
|
||||
rtt_sum += request->rtt();
|
||||
stats.srflx_addrs.insert(request->srflx_addr.ToString());
|
||||
srflx_ips.insert(request->srflx_addr.ipaddr());
|
||||
}
|
||||
|
||||
// If we're using shared mode and seeing >1 srflx addresses for a single
|
||||
// requester, it's symmetric NAT.
|
||||
if (shared_socket_mode_ && num_response_per_srflx_addr.size() > 1) {
|
||||
nat_type = NATTYPE_SYMMETRIC;
|
||||
}
|
||||
}
|
||||
|
||||
// We're probably not behind a regular NAT. We have more than 1 distinct
|
||||
// server reflexive IPs.
|
||||
if (srflx_ips.size() > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int num_sent = 0;
|
||||
int num_received = 0;
|
||||
int num_server_ip_with_response = 0;
|
||||
|
||||
for (const auto& kv : num_response_per_server) {
|
||||
RTC_DCHECK_GT(kv.second, 0);
|
||||
num_server_ip_with_response++;
|
||||
num_received += kv.second;
|
||||
num_sent += num_request_per_server[kv.first];
|
||||
}
|
||||
|
||||
// Shared mode is only true if we use the shared socket and there are more
|
||||
// than 1 responding servers.
|
||||
stats.shared_socket_mode =
|
||||
shared_socket_mode_ && (num_server_ip_with_response > 1);
|
||||
|
||||
if (stats.shared_socket_mode && nat_type == NATTYPE_INVALID) {
|
||||
nat_type = NATTYPE_NON_SYMMETRIC;
|
||||
}
|
||||
|
||||
// If we could find a local IP matching srflx, we're not behind a NAT.
|
||||
rtc::SocketAddress srflx_addr;
|
||||
if (stats.srflx_addrs.size() &&
|
||||
!srflx_addr.FromString(*(stats.srflx_addrs.begin()))) {
|
||||
return false;
|
||||
}
|
||||
for (const auto* net : networks_) {
|
||||
if (srflx_addr.ipaddr() == net->GetBestIP()) {
|
||||
nat_type = stunprober::NATTYPE_NONE;
|
||||
stats.host_ip = net->GetBestIP().ToString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, we know we're behind a NAT but can't determine which type it is.
|
||||
if (nat_type == NATTYPE_INVALID) {
|
||||
nat_type = NATTYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
stats.nat_type = nat_type;
|
||||
stats.num_request_sent = num_sent;
|
||||
stats.num_response_received = num_received;
|
||||
stats.target_request_interval_ns = interval_ms_ * 1000;
|
||||
|
||||
if (num_sent) {
|
||||
stats.success_percent = static_cast<int>(100 * num_received / num_sent);
|
||||
}
|
||||
|
||||
if (stats.raw_num_request_sent > 1) {
|
||||
stats.actual_request_interval_ns =
|
||||
(1000 * (last_sent_time - first_sent_time)) /
|
||||
(stats.raw_num_request_sent - 1);
|
||||
}
|
||||
|
||||
if (num_received) {
|
||||
stats.average_rtt_ms = static_cast<int>((rtt_sum / num_received));
|
||||
}
|
||||
|
||||
*prob_stats = stats;
|
||||
return true;
|
||||
}
|
||||
|
||||
void StunProber::ReportOnPrepared(StunProber::Status status) {
|
||||
if (observer_) {
|
||||
observer_->OnPrepared(this, status);
|
||||
}
|
||||
}
|
||||
|
||||
void StunProber::ReportOnFinished(StunProber::Status status) {
|
||||
if (observer_) {
|
||||
observer_->OnFinished(this, status);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace stunprober
|
||||
251
TMessagesProj/jni/voip/webrtc/p2p/stunprober/stun_prober.h
Normal file
251
TMessagesProj/jni/voip/webrtc/p2p/stunprober/stun_prober.h
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* Copyright 2015 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef P2P_STUNPROBER_STUN_PROBER_H_
|
||||
#define P2P_STUNPROBER_STUN_PROBER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/async_dns_resolver.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "api/task_queue/pending_task_safety_flag.h"
|
||||
#include "rtc_base/network.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/system/rtc_export.h"
|
||||
#include "rtc_base/thread.h"
|
||||
|
||||
namespace rtc {
|
||||
class AsyncPacketSocket;
|
||||
class PacketSocketFactory;
|
||||
class Thread;
|
||||
class NetworkManager;
|
||||
class AsyncResolverInterface;
|
||||
} // namespace rtc
|
||||
|
||||
namespace stunprober {
|
||||
|
||||
class StunProber;
|
||||
|
||||
static const int kMaxUdpBufferSize = 1200;
|
||||
|
||||
typedef std::function<void(StunProber*, int)> AsyncCallback;
|
||||
|
||||
enum NatType {
|
||||
NATTYPE_INVALID,
|
||||
NATTYPE_NONE, // Not behind a NAT.
|
||||
NATTYPE_UNKNOWN, // Behind a NAT but type can't be determine.
|
||||
NATTYPE_SYMMETRIC, // Behind a symmetric NAT.
|
||||
NATTYPE_NON_SYMMETRIC // Behind a non-symmetric NAT.
|
||||
};
|
||||
|
||||
class RTC_EXPORT StunProber : public sigslot::has_slots<> {
|
||||
public:
|
||||
enum Status { // Used in UMA_HISTOGRAM_ENUMERATION.
|
||||
SUCCESS, // Successfully received bytes from the server.
|
||||
GENERIC_FAILURE, // Generic failure.
|
||||
RESOLVE_FAILED, // Host resolution failed.
|
||||
WRITE_FAILED, // Sending a message to the server failed.
|
||||
READ_FAILED, // Reading the reply from the server failed.
|
||||
};
|
||||
|
||||
class Observer {
|
||||
public:
|
||||
virtual ~Observer() = default;
|
||||
virtual void OnPrepared(StunProber* prober, StunProber::Status status) = 0;
|
||||
virtual void OnFinished(StunProber* prober, StunProber::Status status) = 0;
|
||||
};
|
||||
|
||||
struct RTC_EXPORT Stats {
|
||||
Stats();
|
||||
~Stats();
|
||||
|
||||
// `raw_num_request_sent` is the total number of requests
|
||||
// sent. `num_request_sent` is the count of requests against a server where
|
||||
// we see at least one response. `num_request_sent` is designed to protect
|
||||
// against DNS resolution failure or the STUN server is not responsive
|
||||
// which could skew the result.
|
||||
int raw_num_request_sent = 0;
|
||||
int num_request_sent = 0;
|
||||
|
||||
int num_response_received = 0;
|
||||
NatType nat_type = NATTYPE_INVALID;
|
||||
int average_rtt_ms = -1;
|
||||
int success_percent = 0;
|
||||
int target_request_interval_ns = 0;
|
||||
int actual_request_interval_ns = 0;
|
||||
|
||||
// Also report whether this trial can't be considered truly as shared
|
||||
// mode. Share mode only makes sense when we have multiple IP resolved and
|
||||
// successfully probed.
|
||||
bool shared_socket_mode = false;
|
||||
|
||||
std::string host_ip;
|
||||
|
||||
// If the srflx_addrs has more than 1 element, the NAT is symmetric.
|
||||
std::set<std::string> srflx_addrs;
|
||||
};
|
||||
|
||||
StunProber(rtc::PacketSocketFactory* socket_factory,
|
||||
rtc::Thread* thread,
|
||||
std::vector<const rtc::Network*> networks);
|
||||
~StunProber() override;
|
||||
|
||||
StunProber(const StunProber&) = delete;
|
||||
StunProber& operator=(const StunProber&) = delete;
|
||||
|
||||
// Begin performing the probe test against the `servers`. If
|
||||
// `shared_socket_mode` is false, each request will be done with a new socket.
|
||||
// Otherwise, a unique socket will be used for a single round of requests
|
||||
// against all resolved IPs. No single socket will be used against a given IP
|
||||
// more than once. The interval of requests will be as close to the requested
|
||||
// inter-probe interval `stun_ta_interval_ms` as possible. After sending out
|
||||
// the last scheduled request, the probe will wait `timeout_ms` for request
|
||||
// responses and then call `finish_callback`. `requests_per_ip` indicates how
|
||||
// many requests should be tried for each resolved IP address. In shared mode,
|
||||
// (the number of sockets to be created) equals to `requests_per_ip`. In
|
||||
// non-shared mode, (the number of sockets) equals to requests_per_ip * (the
|
||||
// number of resolved IP addresses). TODO(guoweis): Remove this once
|
||||
// everything moved to Prepare() and Run().
|
||||
bool Start(const std::vector<rtc::SocketAddress>& servers,
|
||||
bool shared_socket_mode,
|
||||
int stun_ta_interval_ms,
|
||||
int requests_per_ip,
|
||||
int timeout_ms,
|
||||
AsyncCallback finish_callback);
|
||||
|
||||
// TODO(guoweis): The combination of Prepare() and Run() are equivalent to the
|
||||
// Start() above. Remove Start() once everything is migrated.
|
||||
bool Prepare(const std::vector<rtc::SocketAddress>& servers,
|
||||
bool shared_socket_mode,
|
||||
int stun_ta_interval_ms,
|
||||
int requests_per_ip,
|
||||
int timeout_ms,
|
||||
StunProber::Observer* observer);
|
||||
|
||||
// Start to send out the STUN probes.
|
||||
bool Start(StunProber::Observer* observer);
|
||||
|
||||
// Method to retrieve the Stats once `finish_callback` is invoked. Returning
|
||||
// false when the result is inconclusive, for example, whether it's behind a
|
||||
// NAT or not.
|
||||
bool GetStats(Stats* stats) const;
|
||||
|
||||
int estimated_execution_time() {
|
||||
return static_cast<int>(requests_per_ip_ * all_servers_addrs_.size() *
|
||||
interval_ms_);
|
||||
}
|
||||
|
||||
private:
|
||||
// A requester tracks the requests and responses from a single socket to many
|
||||
// STUN servers.
|
||||
class Requester;
|
||||
|
||||
// TODO(guoweis): Remove this once all dependencies move away from
|
||||
// AsyncCallback.
|
||||
class ObserverAdapter : public Observer {
|
||||
public:
|
||||
ObserverAdapter();
|
||||
~ObserverAdapter() override;
|
||||
|
||||
void set_callback(AsyncCallback callback) { callback_ = callback; }
|
||||
void OnPrepared(StunProber* stunprober, Status status) override;
|
||||
void OnFinished(StunProber* stunprober, Status status) override;
|
||||
|
||||
private:
|
||||
AsyncCallback callback_;
|
||||
};
|
||||
|
||||
bool ResolveServerName(const rtc::SocketAddress& addr);
|
||||
void OnServerResolved(const webrtc::AsyncDnsResolverResult& resolver);
|
||||
|
||||
void OnSocketReady(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::SocketAddress& addr);
|
||||
|
||||
void CreateSockets();
|
||||
|
||||
bool Done() {
|
||||
return num_request_sent_ >= requests_per_ip_ * all_servers_addrs_.size();
|
||||
}
|
||||
|
||||
size_t total_socket_required() {
|
||||
return (shared_socket_mode_ ? 1 : all_servers_addrs_.size()) *
|
||||
requests_per_ip_;
|
||||
}
|
||||
|
||||
bool should_send_next_request(int64_t now);
|
||||
int get_wake_up_interval_ms();
|
||||
|
||||
bool SendNextRequest();
|
||||
|
||||
// Will be invoked in 1ms intervals and schedule the next request from the
|
||||
// `current_requester_` if the time has passed for another request.
|
||||
void MaybeScheduleStunRequests();
|
||||
|
||||
void ReportOnPrepared(StunProber::Status status);
|
||||
void ReportOnFinished(StunProber::Status status);
|
||||
|
||||
Requester* CreateRequester();
|
||||
|
||||
Requester* current_requester_ = nullptr;
|
||||
|
||||
// The time when the next request should go out.
|
||||
int64_t next_request_time_ms_ = 0;
|
||||
|
||||
// Total requests sent so far.
|
||||
uint32_t num_request_sent_ = 0;
|
||||
|
||||
bool shared_socket_mode_ = false;
|
||||
|
||||
// How many requests should be done against each resolved IP.
|
||||
uint32_t requests_per_ip_ = 0;
|
||||
|
||||
// Milliseconds to pause between each STUN request.
|
||||
int interval_ms_;
|
||||
|
||||
// Timeout period after the last request is sent.
|
||||
int timeout_ms_;
|
||||
|
||||
// STUN server name to be resolved.
|
||||
std::vector<rtc::SocketAddress> servers_;
|
||||
|
||||
// Weak references.
|
||||
rtc::PacketSocketFactory* socket_factory_;
|
||||
rtc::Thread* thread_;
|
||||
|
||||
// Accumulate all resolved addresses.
|
||||
std::vector<rtc::SocketAddress> all_servers_addrs_;
|
||||
|
||||
// The set of STUN probe sockets and their state.
|
||||
std::vector<Requester*> requesters_;
|
||||
|
||||
webrtc::SequenceChecker thread_checker_;
|
||||
|
||||
// Temporary storage for created sockets.
|
||||
std::vector<rtc::AsyncPacketSocket*> sockets_;
|
||||
// This tracks how many of the sockets are ready.
|
||||
size_t total_ready_sockets_ = 0;
|
||||
|
||||
Observer* observer_ = nullptr;
|
||||
// TODO(guoweis): Remove this once all dependencies move away from
|
||||
// AsyncCallback.
|
||||
ObserverAdapter observer_adapter_;
|
||||
|
||||
const std::vector<const rtc::Network*> networks_;
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface> resolver_;
|
||||
|
||||
webrtc::ScopedTaskSafety task_safety_;
|
||||
};
|
||||
|
||||
} // namespace stunprober
|
||||
|
||||
#endif // P2P_STUNPROBER_STUN_PROBER_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue