Integrating latest 47acbe8
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<PacketGroup Name="CorePackets" PacketStart="0">
|
||||
<Packet Name="InitiateConnectionPacket" Desc="This packet is used to initiate a new connection" />
|
||||
<Packet Name="InitiateConnectionPacket" Desc="This packet is used to initiate a new connection">
|
||||
<Member Type="AzNetworking::UdpPacketEncodingBuffer" Name="handshakeBuffer" />
|
||||
</Packet>
|
||||
|
||||
<Packet Name="ConnectionHandshakePacket" Desc="This packet is used to negotiate the handshake of a new connection">
|
||||
<Member Type="AzNetworking::UdpPacketEncodingBuffer" Name="handshakeBuffer" />
|
||||
</Packet>
|
||||
|
||||
<Packet Name="TerminateConnectionPacket" Desc="This packet is used to gracefully terminate an existing connection">
|
||||
<Member Type="AzNetworking::DisconnectReason" Name="disconnectReason" Init="AzNetworking::DisconnectReason::None" />
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzCore/PlatformDef.h>
|
||||
#include <AzCore/std/functional.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
@@ -92,7 +93,14 @@ namespace AzNetworking
|
||||
{
|
||||
public:
|
||||
virtual ~ICompressorFactory() = default;
|
||||
|
||||
//! Instantiate a new compressor
|
||||
//! @return A unique_ptr to a new Compressor
|
||||
virtual AZStd::unique_ptr<ICompressor> Create() = 0;
|
||||
|
||||
//! Gets the AZ Name of this compressor factory
|
||||
//! @return the AZ Name of this compressor factory
|
||||
virtual AZ::Name GetFactoryName() const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzNetworking/Framework/ICompressor.h>
|
||||
#include <AzNetworking/Framework/INetworkInterface.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnection.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnectionListener.h>
|
||||
@@ -45,5 +46,19 @@ namespace AzNetworking
|
||||
//! @param name the name of the network interface to destroy
|
||||
//! @return boolean true on success or false on failure
|
||||
virtual bool DestroyNetworkInterface(AZ::Name name) = 0;
|
||||
|
||||
//! Registers a Compressor Factory that can be used to create compressors for INetworkInterfaces
|
||||
//! @param factory The ICompressorFactory to register
|
||||
virtual void RegisterCompressorFactory(ICompressorFactory* factory) = 0;
|
||||
|
||||
//! Creates a compressor using a registered factory looked up by name
|
||||
//! @param name The name of the Compressor Factory to use, must match result of factory->GetFactoryName()
|
||||
//! @return A unique_ptr to the new compressor
|
||||
virtual AZStd::unique_ptr<ICompressor> CreateCompressor(AZ::Name name) = 0;
|
||||
|
||||
//! Unregisters the compressor factory
|
||||
//! @param name The name of the Compressor factory to unregister, must match result of factory->GetFactoryName()
|
||||
//! @return Whether the factory was found and unregistered
|
||||
virtual bool UnregisterCompressorFactory(AZ::Name name) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace AzNetworking
|
||||
AZ::TimeMs m_sendTimeMs = AZ::TimeMs{ 0 };
|
||||
//! Returns the total number of packets sent on this socket.
|
||||
uint64_t m_sendPackets = 0;
|
||||
//! Returns the total number of encrypted packets sent on this socket.
|
||||
uint64_t m_sendPacketsEncrypted = 0;
|
||||
//! Returns the total number of bytes sent on this socket after compression.
|
||||
uint64_t m_sendBytes = 0;
|
||||
//! Returns the total number of bytes sent on this socket before compression.
|
||||
@@ -34,6 +36,8 @@ namespace AzNetworking
|
||||
uint64_t m_sendCompressedPacketsNoGain = 0;
|
||||
//! Returns the delta gain of bytes saved (+) or lost (-) due to compression.
|
||||
int64_t m_sendBytesCompressedDelta = 0;
|
||||
//! Returns the numbers of bytes added by encryption.
|
||||
uint64_t m_sendBytesEncryptionInflation = 0;
|
||||
//! Returns the total number of packets that had to be resent on this network interface due to packet loss.
|
||||
uint64_t m_resentPackets = 0;
|
||||
//! Returns the total number of milliseconds spent processing received data on this network interface.
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace AzNetworking
|
||||
NetworkingSystemComponent::NetworkingSystemComponent()
|
||||
{
|
||||
SocketLayerInit();
|
||||
//EncryptionLayerInit();
|
||||
EncryptionLayerInit();
|
||||
AZ::Interface<INetworking>::Register(this);
|
||||
|
||||
m_listenThread = AZStd::make_unique<TcpListenThread>();
|
||||
@@ -53,11 +53,14 @@ namespace AzNetworking
|
||||
{
|
||||
// Delete all our network interfaces first so they can unregister from the reader and listen threads
|
||||
m_networkInterfaces.clear();
|
||||
|
||||
m_compressorFactories.clear();
|
||||
|
||||
m_readerThread = nullptr;
|
||||
m_listenThread = nullptr;
|
||||
|
||||
AZ::Interface<INetworking>::Unregister(this);
|
||||
//EncryptionLayerShutdown();
|
||||
EncryptionLayerShutdown();
|
||||
SocketLayerShutdown();
|
||||
}
|
||||
|
||||
@@ -123,6 +126,29 @@ namespace AzNetworking
|
||||
return m_networkInterfaces.erase(name) > 0;
|
||||
}
|
||||
|
||||
void NetworkingSystemComponent::RegisterCompressorFactory(ICompressorFactory* factory)
|
||||
{
|
||||
AZ_Assert(m_compressorFactories.find(factory->GetFactoryName()) == m_compressorFactories.end(), "A compressor factory with this name already exists");
|
||||
|
||||
m_compressorFactories.emplace(factory->GetFactoryName(), factory);
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<ICompressor> NetworkingSystemComponent::CreateCompressor(AZ::Name name)
|
||||
{
|
||||
auto compressorFactory = m_compressorFactories.find(name);
|
||||
if(compressorFactory != m_compressorFactories.end())
|
||||
{
|
||||
return compressorFactory->second->Create();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool NetworkingSystemComponent::UnregisterCompressorFactory(AZ::Name name)
|
||||
{
|
||||
return m_compressorFactories.erase(name) > 0;
|
||||
}
|
||||
|
||||
void NetworkingSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
|
||||
{
|
||||
AZLOG_INFO("Total sockets monitored by TcpListenThread: %u", m_listenThread->GetSocketCount());
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzNetworking/Framework/ICompressor.h>
|
||||
#include <AzNetworking/Framework/INetworking.h>
|
||||
#include <AzNetworking/Framework/INetworkInterface.h>
|
||||
#include <AzNetworking/TcpTransport/TcpListenThread.h>
|
||||
@@ -59,6 +60,9 @@ namespace AzNetworking
|
||||
INetworkInterface* CreateNetworkInterface(AZ::Name name, ProtocolType protocolType, TrustZone trustZone, IConnectionListener& listener) override;
|
||||
INetworkInterface* RetrieveNetworkInterface(AZ::Name name) override;
|
||||
bool DestroyNetworkInterface(AZ::Name name) override;
|
||||
void RegisterCompressorFactory(ICompressorFactory* factory) override;
|
||||
AZStd::unique_ptr<ICompressor> CreateCompressor(AZ::Name name) override;
|
||||
bool UnregisterCompressorFactory(AZ::Name name) override;
|
||||
//! @}
|
||||
|
||||
//! Console commands.
|
||||
@@ -74,5 +78,8 @@ namespace AzNetworking
|
||||
NetworkInterfaces m_networkInterfaces;
|
||||
AZStd::unique_ptr<TcpListenThread> m_listenThread;
|
||||
AZStd::unique_ptr<UdpReaderThread> m_readerThread;
|
||||
|
||||
using CompressionFactories = AZStd::unordered_map<AZ::Name, AZStd::unique_ptr<ICompressorFactory>>;
|
||||
CompressionFactories m_compressorFactories;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzNetworking/Framework/INetworking.h>
|
||||
#include <AzNetworking/TcpTransport/TcpConnection.h>
|
||||
#include <AzNetworking/TcpTransport/TcpPacketHeader.h>
|
||||
#include <AzNetworking/TcpTransport/TcpNetworkInterface.h>
|
||||
@@ -17,7 +18,6 @@
|
||||
#include <AzNetworking/Serialization/NetworkOutputSerializer.h>
|
||||
#include <AzNetworking/ConnectionLayer/IConnectionSet.h>
|
||||
#include <AzNetworking/Framework/ICompressor.h>
|
||||
#include <AzNetworking/Utilities/CompressionCommon.h>
|
||||
#include <AzNetworking/AutoGen/CorePackets.AutoPackets.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
@@ -61,8 +61,8 @@ namespace AzNetworking
|
||||
, m_registeredSocketFd(InvalidSocketFd)
|
||||
{
|
||||
const AZ::CVarFixedString compressor = static_cast<AZ::CVarFixedString>(net_TcpCompressor);
|
||||
const char* compressorName = compressor.c_str();
|
||||
m_compressor = CreateCompressor(compressorName);
|
||||
const AZ::Name compressorName = AZ::Name(compressor);
|
||||
m_compressor = AZ::Interface<INetworking>::Get()->CreateCompressor(compressorName);
|
||||
|
||||
if (useEncryption)
|
||||
{
|
||||
@@ -115,6 +115,12 @@ namespace AzNetworking
|
||||
m_sendRingbuffer.AdvanceReadBuffer(sentBytes);
|
||||
m_networkInterface.GetMetrics().m_sendBytes += numSendBytes;
|
||||
m_networkInterface.GetMetrics().m_sendBytesUncompressed += numSendBytes;
|
||||
|
||||
if (m_socket->IsEncrypted() && sentBytes > 0)
|
||||
{
|
||||
m_networkInterface.GetMetrics().m_sendBytesEncryptionInflation += (aznumeric_cast<uint32_t>(sentBytes) - numSendBytes);
|
||||
m_networkInterface.GetMetrics().m_sendPacketsEncrypted++;
|
||||
}
|
||||
}
|
||||
|
||||
bool TcpConnection::UpdateRecv()
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace AzNetworking
|
||||
return true;
|
||||
}
|
||||
|
||||
void TcpSocketManager::ProcessEvents(AZ::TimeMs maxBlockMs, const SocketEventCallback& readCallback, const SocketEventCallback& writeCallback)
|
||||
void TcpSocketManager::ProcessEvents([[maybe_unused]]AZ::TimeMs maxBlockMs, const SocketEventCallback& readCallback, const SocketEventCallback& writeCallback)
|
||||
{
|
||||
// No edge triggering, just brute force iterate all socketFds and invoke the callbacks
|
||||
for (auto socketFd : m_socketFds)
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace AzNetworking
|
||||
TcpSocket::Close();
|
||||
}
|
||||
|
||||
int32_t TlsSocket::SendInternal(const uint8_t* data, uint32_t size) const
|
||||
int32_t TlsSocket::SendInternal([[maybe_unused]] const uint8_t* data, [[maybe_unused]] uint32_t size) const
|
||||
{
|
||||
if (m_sslSocket == nullptr)
|
||||
{
|
||||
@@ -201,7 +201,7 @@ namespace AzNetworking
|
||||
#endif
|
||||
}
|
||||
|
||||
int32_t TlsSocket::ReceiveInternal(uint8_t* outData, uint32_t size) const
|
||||
int32_t TlsSocket::ReceiveInternal([[maybe_unused]] uint8_t* outData, [[maybe_unused]] uint32_t size) const
|
||||
{
|
||||
if (m_sslSocket == nullptr)
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <AzNetworking/UdpTransport/DtlsEndpoint.h>
|
||||
#include <AzNetworking/UdpTransport/DtlsSocket.h>
|
||||
#include <AzNetworking/UdpTransport/UdpConnection.h>
|
||||
#include <AzNetworking/Utilities/EncryptionCommon.h>
|
||||
#include <AzNetworking/Utilities/NetworkIncludes.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
@@ -57,14 +58,8 @@ namespace AzNetworking
|
||||
return result;
|
||||
}
|
||||
|
||||
DtlsEndpoint::ConnectResult DtlsEndpoint::Accept(const DtlsSocket& socket, const IpAddress& address, const UdpPacketEncodingBuffer& dtlsData)
|
||||
DtlsEndpoint::ConnectResult DtlsEndpoint::Accept(const DtlsSocket& socket, const IpAddress& address)
|
||||
{
|
||||
if (dtlsData.GetSize() <= 0)
|
||||
{
|
||||
AZLOG_WARN("Encryption is enabled on accepting endpoint, but connector provided an empty DTLS handshake blob. Check that encryption is properly disabled on *BOTH* endpoints");
|
||||
return DtlsEndpoint::ConnectResult::Failed;
|
||||
}
|
||||
|
||||
const ConnectResult result = ConstructEndpointInternal(socket, address);
|
||||
#if AZ_TRAIT_USE_OPENSSL
|
||||
if (result != ConnectResult::Failed)
|
||||
@@ -72,10 +67,7 @@ namespace AzNetworking
|
||||
// This SSL should be configured to accept connections
|
||||
SSL_set_accept_state(m_sslSocket);
|
||||
m_state = HandshakeState::Accepting;
|
||||
const uint8_t* encryptedData = dtlsData.GetBuffer();
|
||||
const uint32_t encryptedSize = dtlsData.GetSize();
|
||||
BIO_write(m_readBio, encryptedData, encryptedSize);
|
||||
return CompleteHandshake(socket);
|
||||
return ConnectResult::Pending;
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
@@ -88,49 +80,53 @@ namespace AzNetworking
|
||||
|| (m_state == HandshakeState::Failed)); // In all cases caller should call CompleteHandshake() next and check the return value
|
||||
}
|
||||
|
||||
DtlsEndpoint::ConnectResult DtlsEndpoint::CompleteHandshake(const UdpSocket& socket)
|
||||
DtlsEndpoint::ConnectResult DtlsEndpoint::ProcessHandshakeData([[maybe_unused]] UdpConnection& connection, [[maybe_unused]] const UdpPacketEncodingBuffer& dtlsData)
|
||||
{
|
||||
UdpPacketEncodingBuffer responseData;
|
||||
const ConnectResult result = PerformHandshakeInternal(responseData);
|
||||
if ((result != ConnectResult::Failed) && (responseData.GetSize() > 0))
|
||||
ConnectResult result = ConnectResult::Failed;
|
||||
#if AZ_TRAIT_USE_OPENSSL
|
||||
UdpPacketEncodingBuffer outDtlsData;
|
||||
if (dtlsData.GetSize() > 0)
|
||||
{
|
||||
struct sockaddr_in dest;
|
||||
memset(&dest, 0, sizeof(dest));
|
||||
dest.sin_family = AF_INET;
|
||||
dest.sin_addr.s_addr = m_address.GetAddress(ByteOrder::Network);
|
||||
dest.sin_port = m_address.GetPort(ByteOrder::Network);
|
||||
sendto(static_cast<int32_t>(socket.GetSocketFd()), reinterpret_cast<char*>(responseData.GetBuffer()), responseData.GetSize(), 0, (sockaddr*)&dest, sizeof(dest));
|
||||
AZLOG(NET_DebugDtls, "Replying to DTLS handshake datagram, %u bytes", static_cast<int32_t>(responseData.GetSize()));
|
||||
const uint8_t* encryptedData = dtlsData.GetBuffer();
|
||||
const uint32_t encryptedSize = dtlsData.GetSize();
|
||||
BIO_write(m_readBio, encryptedData, encryptedSize);
|
||||
}
|
||||
DtlsEndpoint::HandshakeState prevState = m_state;
|
||||
result = PerformHandshakeInternal(outDtlsData);
|
||||
|
||||
// Pass along any remaining handshake data
|
||||
// If we're the connecting endpoint and the handshake is complete, both sides are encrypted and this isn't necessary so skip it
|
||||
bool continueHandshake = prevState != DtlsEndpoint::HandshakeState::Connecting || m_state != DtlsEndpoint::HandshakeState::Complete;
|
||||
if (outDtlsData.GetSize() > 0 && continueHandshake)
|
||||
{
|
||||
CorePackets::ConnectionHandshakePacket handshakePacket = CorePackets::ConnectionHandshakePacket();
|
||||
handshakePacket.SetHandshakeBuffer(outDtlsData);
|
||||
// SSL prefers we handle resend by reobtaining data through SSL so we use unreliable and resend on timeout
|
||||
connection.SendUnreliablePacket(handshakePacket);
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const uint8_t* DtlsEndpoint::DecodePacket
|
||||
(
|
||||
[[maybe_unused]] const UdpSocket& socket,
|
||||
[[maybe_unused]] const uint8_t* encryptedData,
|
||||
[[maybe_unused]] int32_t encryptedSize,
|
||||
[[maybe_unused]] uint8_t* outDecodedData,
|
||||
[[maybe_unused]] int32_t& outDecodedSize
|
||||
)
|
||||
const uint8_t* DtlsEndpoint::DecodePacket([[maybe_unused]] UdpConnection& connection, const uint8_t* encryptedData, int32_t encryptedSize, uint8_t* outDecodedData, int32_t& outDecodedSize)
|
||||
{
|
||||
if (m_sslSocket == nullptr)
|
||||
if (m_sslSocket == nullptr || IsConnecting())
|
||||
{
|
||||
// If the ssl socket is nullptr, it means encryption is not enabled, just passthrough the received data
|
||||
// If the socket is connecting/handshaking, it means we can't yet encrypt, just passthrough the received data
|
||||
outDecodedSize = encryptedSize;
|
||||
return encryptedData;
|
||||
}
|
||||
#if AZ_TRAIT_USE_OPENSSL
|
||||
BIO_write(m_readBio, encryptedData, encryptedSize);
|
||||
if (IsConnecting())
|
||||
{
|
||||
CompleteHandshake(socket);
|
||||
outDecodedSize = 0;
|
||||
}
|
||||
// CompleteHandshake() above may have failed and destroyed the SSL context, check here that state is valid so we don't crash on SSL_read
|
||||
|
||||
int32_t bioWriteSize = BIO_write(m_readBio, encryptedData, encryptedSize);
|
||||
if (m_state != HandshakeState::Failed)
|
||||
{
|
||||
if (bioWriteSize != encryptedSize)
|
||||
{
|
||||
AZLOG_ERROR("BIO did not write as many bytes as provided");
|
||||
}
|
||||
outDecodedSize = SSL_read(m_sslSocket, outDecodedData, encryptedSize);
|
||||
}
|
||||
#endif
|
||||
@@ -175,13 +171,6 @@ namespace AzNetworking
|
||||
|
||||
ConnectResult connectResult = ConnectResult::Pending;
|
||||
#if AZ_TRAIT_USE_OPENSSL
|
||||
if (SSL_is_init_finished(m_sslSocket))
|
||||
{
|
||||
const char* stateString = GetEnumString(m_state);
|
||||
AZLOG(NET_DebugDtls, "dtls handshake is completed, unblocking connection for game traffic, prior state: %s", stateString);
|
||||
m_state = HandshakeState::Complete;
|
||||
connectResult = ConnectResult::Complete;
|
||||
}
|
||||
ERR_clear_error();
|
||||
const int32_t result = SSL_do_handshake(m_sslSocket);
|
||||
if (result <= 0)
|
||||
@@ -200,6 +189,14 @@ namespace AzNetworking
|
||||
}
|
||||
}
|
||||
|
||||
if (SSL_is_init_finished(m_sslSocket))
|
||||
{
|
||||
const char* stateString = GetEnumString(m_state);
|
||||
AZLOG(NET_DebugDtls, "dtls handshake is completed, unblocking connection for game traffic, prior state: %s", stateString);
|
||||
m_state = HandshakeState::Complete;
|
||||
connectResult = ConnectResult::Complete;
|
||||
}
|
||||
|
||||
// Need to do this... connection negotiation may have left data in the write bio that we need to send out
|
||||
if (BIO_ctrl_pending(m_writeBio) > 0)
|
||||
{
|
||||
|
||||
@@ -22,6 +22,7 @@ typedef struct bio_st BIO;
|
||||
|
||||
namespace AzNetworking
|
||||
{
|
||||
class UdpConnection;
|
||||
class UdpSocket;
|
||||
class DtlsSocket;
|
||||
|
||||
@@ -61,30 +62,30 @@ namespace AzNetworking
|
||||
ConnectResult Connect(const DtlsSocket& socket, const IpAddress& address, UdpPacketEncodingBuffer& outDtlsData);
|
||||
|
||||
//! Accepts a connection from the remote encrypted endpoint.
|
||||
//! @param socket the dtls socket being used for data transmission
|
||||
//! @param address the address of the remote endpoint connecting to us
|
||||
//! @param dtlsData data buffer containing the initial dtls handshake packet
|
||||
//! @param socket the dtls socket being used for data transmission
|
||||
//! @param address the address of the remote endpoint connecting to us
|
||||
//! @return a connect result specifying whether the connection is still pending, failed, or complete
|
||||
ConnectResult Accept(const DtlsSocket& socket, const IpAddress& address, const UdpPacketEncodingBuffer& dtlsData);
|
||||
ConnectResult Accept(const DtlsSocket& socket, const IpAddress& address);
|
||||
|
||||
//! Returns whether or not the endpoint is still negotiating the dtls handshake.
|
||||
//! @return true if the endpoint is still in a connecting state
|
||||
bool IsConnecting() const;
|
||||
|
||||
//! Attempts to complete the dtls handshake and establish an encrypted connection.
|
||||
//! @param socket the dtls socket being used for data transmission
|
||||
//! @param connection the UDP connection being used for data transmission
|
||||
//! @param dtlsData data buffer containing dtls handshake packet
|
||||
//! @return a connect result specifying whether the connection is still pending, failed, or complete
|
||||
ConnectResult CompleteHandshake(const UdpSocket& socket);
|
||||
ConnectResult ProcessHandshakeData(UdpConnection& connection, const UdpPacketEncodingBuffer& dtlsData);
|
||||
|
||||
//! If the endpoint has encryption enabled, this will decrypt the transmitted data and return the result.
|
||||
//! @note sizes have to be signed since OpenSSL often returns negative values to represent error results
|
||||
//! @param socket the DTLS socket being used for data transmission
|
||||
//! @param connection the UDP connection being used for data transmission
|
||||
//! @param encryptedData the potentially encrypted data received from the socket
|
||||
//! @param encryptedSize the size of the received raw data
|
||||
//! @param outDecodedData an appropriately sized output buffer to store decrypted data
|
||||
//! @param outDecodedSize the size of the output buffer
|
||||
//! @return pointer to the decoded data
|
||||
const uint8_t* DecodePacket(const UdpSocket& socket, const uint8_t* encryptedData, int32_t encryptedSize, uint8_t* outDecodedData, int32_t& outDecodedSize);
|
||||
const uint8_t* DecodePacket(UdpConnection& connection, const uint8_t* encryptedData, int32_t encryptedSize, uint8_t* outDecodedData, int32_t& outDecodedSize);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzNetworking/Framework/INetworkInterface.h>
|
||||
#include <AzNetworking/UdpTransport/DtlsSocket.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
|
||||
@@ -35,9 +36,9 @@ namespace AzNetworking
|
||||
return dtlsEndpoint.Connect(*this, address, outDtlsData);
|
||||
}
|
||||
|
||||
DtlsEndpoint::ConnectResult DtlsSocket::AcceptDtlsEndpoint(DtlsEndpoint& dtlsEndpoint, const IpAddress& address, const UdpPacketEncodingBuffer& dtlsData) const
|
||||
DtlsEndpoint::ConnectResult DtlsSocket::AcceptDtlsEndpoint(DtlsEndpoint& dtlsEndpoint, const IpAddress& address) const
|
||||
{
|
||||
return dtlsEndpoint.Accept(*this, address, dtlsData);
|
||||
return dtlsEndpoint.Accept(*this, address);
|
||||
}
|
||||
|
||||
bool DtlsSocket::Open(uint16_t port, CanAcceptConnections canAccept, TrustZone trustZone)
|
||||
@@ -91,6 +92,11 @@ namespace AzNetworking
|
||||
// Write out the packet we were requested to send
|
||||
const int32_t sentBytesRaw = SSL_write(dtlsEndpoint.m_sslSocket, data, size);
|
||||
const int32_t sentBytesEnc = BIO_read(dtlsEndpoint.m_writeBio, encrpytedSendBuffer, sizeof(encrpytedSendBuffer));
|
||||
|
||||
// Track encryption metrics
|
||||
m_sentBytesEncryptionInflation += aznumeric_cast<uint32_t>(sentBytesEnc - aznumeric_cast<int32_t>(size));
|
||||
m_sentPacketsEncrypted++;
|
||||
|
||||
return UdpSocket::SendInternal(address, encrpytedSendBuffer, sentBytesEnc, encrypt, dtlsEndpoint);
|
||||
#else
|
||||
return 0;
|
||||
|
||||
@@ -46,9 +46,8 @@ namespace AzNetworking
|
||||
//! Accepts an encryption socket wrapper.
|
||||
//! @param dtlsEndpoint the encryption wrapper instance to create a connection over
|
||||
//! @param address the IP address of the endpoint to connect to
|
||||
//! @param dtlsData data buffer containing the dtls handshake packet
|
||||
//! @return a connect result specifying whether the connection is still pending, failed, or complete
|
||||
DtlsEndpoint::ConnectResult AcceptDtlsEndpoint(DtlsEndpoint& dtlsEndpoint, const IpAddress& address, const UdpPacketEncodingBuffer& dtlsData) const override;
|
||||
DtlsEndpoint::ConnectResult AcceptDtlsEndpoint(DtlsEndpoint& dtlsEndpoint, const IpAddress& address) const override;
|
||||
|
||||
//! Opens the UDP socket on the given port.
|
||||
//! @param port the port number to open the UDP socket on, 0 will bind to any available port
|
||||
|
||||
@@ -66,9 +66,9 @@ namespace AzNetworking
|
||||
}
|
||||
}
|
||||
|
||||
DtlsEndpoint::ConnectResult UdpConnection::CompleteHandshake()
|
||||
DtlsEndpoint::ConnectResult UdpConnection::ProcessHandshakeData(const UdpPacketEncodingBuffer& dtlsData)
|
||||
{
|
||||
const DtlsEndpoint::ConnectResult result = m_dtlsEndpoint.CompleteHandshake(*(m_networkInterface.m_socket));
|
||||
const DtlsEndpoint::ConnectResult result = m_dtlsEndpoint.ProcessHandshakeData(*this, dtlsData);
|
||||
if (result == DtlsEndpoint::ConnectResult::Failed)
|
||||
{
|
||||
Disconnect(DisconnectReason::NetworkError, TerminationEndpoint::Local);
|
||||
@@ -249,6 +249,27 @@ namespace AzNetworking
|
||||
}
|
||||
break;
|
||||
|
||||
case CorePackets::PacketType::ConnectionHandshakePacket:
|
||||
{
|
||||
AZLOG(NET_CorePackets, "Received core packet %s", "ConnectionHandshakePacket");
|
||||
CorePackets::ConnectionHandshakePacket packet;
|
||||
if (!serializer.Serialize(packet, "Packet"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_state != ConnectionState::Connected)
|
||||
{
|
||||
if (ProcessHandshakeData(packet.GetHandshakeBuffer()) == DtlsEndpoint::ConnectResult::Complete)
|
||||
{
|
||||
m_state = ConnectionState::Connected;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case CorePackets::PacketType::TerminateConnectionPacket:
|
||||
{
|
||||
AZLOG(NET_CorePackets, "Received core packet %s", "TerminateConnection");
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace AzNetworking
|
||||
class UdpConnection
|
||||
: public IConnection
|
||||
{
|
||||
friend class UdpFragmentQueue;
|
||||
friend class UdpNetworkInterface;
|
||||
|
||||
public:
|
||||
@@ -50,9 +51,10 @@ namespace AzNetworking
|
||||
UdpConnection(ConnectionId connectionId, const IpAddress& remoteAddress, UdpNetworkInterface& networkInterface, ConnectionRole connectionRole);
|
||||
~UdpConnection() override;
|
||||
|
||||
//! Helper to complete dtls handshake logic on a newly established connection
|
||||
//! Helper to exchange dtls handshake data during connection handshake
|
||||
//! @param dtlsData data buffer containing dtls handshake packet
|
||||
//! @return the current result code for the dtls handshake operation, failed, pending, or complete
|
||||
DtlsEndpoint::ConnectResult CompleteHandshake();
|
||||
DtlsEndpoint::ConnectResult ProcessHandshakeData(const UdpPacketEncodingBuffer& dtlsData);
|
||||
|
||||
//! Updates the connection heartbeat if active.
|
||||
//! @param currentTimeMs current wall clock time in milliseconds
|
||||
|
||||
@@ -155,7 +155,17 @@ namespace AzNetworking
|
||||
}
|
||||
}
|
||||
connection->GetPacketTracker().ProcessReceived(connection, header);
|
||||
return connectionListener.OnPacketReceived(connection, header, networkSerializer);
|
||||
bool handledPacket = false;
|
||||
if (header.GetPacketType() < aznumeric_cast<PacketType>(CorePackets::PacketType::MAX))
|
||||
{
|
||||
handledPacket = connection->HandleCorePacket(connectionListener, header, networkSerializer);
|
||||
}
|
||||
else
|
||||
{
|
||||
handledPacket = connectionListener.OnPacketReceived(connection, header, networkSerializer);
|
||||
}
|
||||
|
||||
return handledPacket;
|
||||
}
|
||||
|
||||
TimeoutResult UdpFragmentQueue::HandleTimeout(TimeoutQueue::TimeoutItem& item)
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace AzNetworking
|
||||
class UdpFragmentQueue
|
||||
: public ITimeoutHandler
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
//! Updates the UdpFragmentQueue timeout queue.
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzNetworking/Framework/INetworking.h>
|
||||
#include <AzNetworking/UdpTransport/UdpNetworkInterface.h>
|
||||
#include <AzNetworking/UdpTransport/UdpConnection.h>
|
||||
#include <AzNetworking/UdpTransport/DtlsSocket.h>
|
||||
@@ -17,7 +18,6 @@
|
||||
#include <AzNetworking/Serialization/NetworkInputSerializer.h>
|
||||
#include <AzNetworking/Serialization/NetworkOutputSerializer.h>
|
||||
#include <AzNetworking/Framework/ICompressor.h>
|
||||
#include <AzNetworking/Utilities/CompressionCommon.h>
|
||||
#include <AzNetworking/Utilities/NetworkCommon.h>
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
@@ -26,8 +26,11 @@ namespace AzNetworking
|
||||
{
|
||||
#if AZ_TRAIT_USE_OPENSSL
|
||||
AZ_CVAR(bool, net_UdpUseEncryption, false, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Enable encryption on Udp based connections");
|
||||
AZ_CVAR(uint32_t, net_SslInflationOverhead, 32, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "A SSL fudge overhead value to take out of fragmented packet payloads");
|
||||
|
||||
#else
|
||||
static const bool net_UdpUseEncryption = false;
|
||||
static const uint32_t net_SslInflationOverhead = 0;
|
||||
#endif
|
||||
|
||||
AZ_CVAR(bool, net_UdpTimeoutConnections, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Boolean value on whether we should timeout Udp connections");
|
||||
@@ -64,8 +67,8 @@ namespace AzNetworking
|
||||
, m_readerThread(readerThread)
|
||||
{
|
||||
const AZ::CVarFixedString compressor = static_cast<AZ::CVarFixedString>(net_UdpCompressor);
|
||||
const char* compressorName = compressor.c_str();
|
||||
m_compressor = CreateCompressor(compressorName);
|
||||
const AZ::Name compressorName = AZ::Name(compressor);
|
||||
m_compressor = AZ::Interface<INetworking>::Get()->CreateCompressor(compressorName);
|
||||
}
|
||||
|
||||
UdpNetworkInterface::~UdpNetworkInterface()
|
||||
@@ -122,7 +125,7 @@ namespace AzNetworking
|
||||
{
|
||||
if (!m_socket->IsOpen())
|
||||
{
|
||||
m_socket->Open(m_port, UdpSocket::CanAcceptConnections::True, m_trustZone);
|
||||
m_socket->Open(m_port, UdpSocket::CanAcceptConnections::False, m_trustZone);
|
||||
m_readerThread.RegisterSocket(m_socket.get());
|
||||
}
|
||||
|
||||
@@ -137,7 +140,12 @@ namespace AzNetworking
|
||||
connection->m_state = ConnectionState::Connecting;
|
||||
connection->SetConnectionMtu(MaxUdpTransmissionUnit);
|
||||
connection->SetTimeoutId(timeoutId);
|
||||
connection->SendReliablePacket(CorePackets::InitiateConnectionPacket());
|
||||
|
||||
// Signal the connection attempt
|
||||
CorePackets::InitiateConnectionPacket connectPacket = CorePackets::InitiateConnectionPacket();
|
||||
connectPacket.SetHandshakeBuffer(dtlsData);
|
||||
connection->SendReliablePacket(connectPacket);
|
||||
|
||||
m_connectionListener.OnConnect(connection.get());
|
||||
m_connectionSet.AddConnection(AZStd::move(connection));
|
||||
return connectionId;
|
||||
@@ -150,10 +158,6 @@ namespace AzNetworking
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_LATENCY_DEBUG
|
||||
m_socket->ProcessDeferredPackets();
|
||||
#endif
|
||||
|
||||
const AZ::TimeMs startTimeMs = AZ::GetElapsedTimeMs();
|
||||
const UdpReaderThread::ReceivedPackets* packets = m_readerThread.GetReceivedPackets(m_socket.get());
|
||||
if (packets == nullptr)
|
||||
@@ -191,7 +195,7 @@ namespace AzNetworking
|
||||
|
||||
int32_t decodedPacketSize = 0;
|
||||
m_decryptBuffer.Resize(m_decryptBuffer.GetCapacity());
|
||||
const uint8_t* decodedPacketData = connection->GetDtlsEndpoint().DecodePacket(*m_socket, packet.m_buffer, packet.m_receivedBytes, m_decryptBuffer.GetBuffer(), decodedPacketSize);
|
||||
const uint8_t* decodedPacketData = connection->GetDtlsEndpoint().DecodePacket(*connection, packet.m_buffer, packet.m_receivedBytes, m_decryptBuffer.GetBuffer(), decodedPacketSize);
|
||||
m_decryptBuffer.Resize(decodedPacketSize);
|
||||
|
||||
if (decodedPacketSize == 0)
|
||||
@@ -201,8 +205,7 @@ namespace AzNetworking
|
||||
}
|
||||
else if (decodedPacketSize < 0)
|
||||
{
|
||||
// Something bad happened on the SSL read and we're now invalid, we should disconnect
|
||||
connection->Disconnect(DisconnectReason::SslFailure, TerminationEndpoint::Local);
|
||||
// Late unencrypted handshake packets or just random garbage can show up, discard and continue
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -233,8 +236,8 @@ namespace AzNetworking
|
||||
}
|
||||
decodedPacketData = m_decompressBuffer.GetBuffer();
|
||||
decodedPacketSize = m_decompressBuffer.GetSize();
|
||||
GetMetrics().m_recvBytesUncompressed += decodedPacketSize;
|
||||
}
|
||||
GetMetrics().m_recvBytesUncompressed += decodedPacketSize;
|
||||
|
||||
TimeoutQueue::TimeoutItem* timeoutItem = m_connectionTimeoutQueue.RetrieveItem(connection->GetTimeoutId());
|
||||
if (timeoutItem == nullptr)
|
||||
@@ -273,11 +276,19 @@ namespace AzNetworking
|
||||
if (handledPacket)
|
||||
{
|
||||
connection->UpdateHeartbeat(currentTimeMs);
|
||||
if (connection->GetConnectionState() == ConnectionState::Connecting)
|
||||
if (connection->GetConnectionState() == ConnectionState::Connecting && !connection->GetDtlsEndpoint().IsConnecting())
|
||||
{
|
||||
// Connection is realized once a packet is received and socket handshake is verified complete
|
||||
connection->m_state = ConnectionState::Connected;
|
||||
}
|
||||
}
|
||||
else if (m_socket->IsEncrypted() && connection->GetDtlsEndpoint().IsConnecting() &&
|
||||
!IsHandshakePacket(connection->GetDtlsEndpoint(), header.GetPacketType()))
|
||||
{
|
||||
// It's possible for one side to finish its half of the handshake and start sending encrypted data
|
||||
// If it's not an expected unencrypted type then skip it for now
|
||||
continue;
|
||||
}
|
||||
else if (connection->GetConnectionState() != ConnectionState::Disconnecting)
|
||||
{
|
||||
connection->Disconnect(DisconnectReason::StreamError, TerminationEndpoint::Local);
|
||||
@@ -309,6 +320,8 @@ namespace AzNetworking
|
||||
// Update metrics
|
||||
GetMetrics().m_sendPackets = m_socket->GetSentPackets();
|
||||
GetMetrics().m_sendBytes = m_socket->GetSentBytes();
|
||||
GetMetrics().m_sendPacketsEncrypted = m_socket->GetSentPacketsEncrypted();
|
||||
GetMetrics().m_sendBytesEncryptionInflation = m_socket->GetSentBytesEncryptionInflation();
|
||||
GetMetrics().m_recvTimeMs += receiveTimeMs;
|
||||
GetMetrics().m_recvPackets = m_socket->GetRecvPackets();
|
||||
GetMetrics().m_recvBytes = m_socket->GetRecvBytes();
|
||||
@@ -410,8 +423,7 @@ namespace AzNetworking
|
||||
|
||||
// The ordering inside this function is incredibly important and fragile
|
||||
const IpAddress& address = connection.GetRemoteAddress();
|
||||
// We don't want to compress the initial InitiateConnectionPacket
|
||||
// We can use this to transmit compression and encryption details in the future
|
||||
// We don't want to compress the initial InitiateConnectionPacket, ConnectionHandshakePackets or FragmentedPackets of those two
|
||||
const bool shouldCompress = packet.GetPacketType() != aznumeric_cast<PacketType>(CorePackets::PacketType::InitiateConnectionPacket);
|
||||
|
||||
if (address.GetAddress(ByteOrder::Host) == 0)
|
||||
@@ -427,6 +439,29 @@ namespace AzNetworking
|
||||
UdpPacketHeader header(connection.GetPacketTracker(), packet.GetPacketType(), reliableSequence);
|
||||
const PacketId localPacketId = header.GetPacketId();
|
||||
|
||||
// If it's a reliable packet, make sure our reliable queue knows about it now because we might need to drop it if our connection is
|
||||
// not set up
|
||||
if (reliabilityType == ReliabilityType::Reliable)
|
||||
{
|
||||
if (!connection.PrepareReliablePacketForSend(localPacketId, reliableSequence, packet))
|
||||
{
|
||||
connection.Disconnect(DisconnectReason::ReliableQueueFull, TerminationEndpoint::Local);
|
||||
}
|
||||
}
|
||||
|
||||
// If we're still connecting, only transmit packets related to establishing connection and queue the rest for later
|
||||
// This implicitly enforces that the only FragmentedPackets sent here are of ConnectionHandshakePacket
|
||||
// Other large packets are simply queued before they are fragmented
|
||||
if (connection.GetDtlsEndpoint().IsConnecting() && !IsHandshakePacket(connection.GetDtlsEndpoint(), packet.GetPacketType()))
|
||||
{
|
||||
// IMPORTANT that we register with the timeout queue here, otherwise we don't have the timer to pop for reliable packets
|
||||
RegisterWithTimeoutQueue(connection.GetConnectionId(), localPacketId, reliabilityType, connection.GetMetrics());
|
||||
AZLOG(
|
||||
NET_DebugDtls, "Connection is still in handshake negotiation, blocking packet send for packet type %d",
|
||||
(int)packet.GetPacketType());
|
||||
return localPacketId;
|
||||
}
|
||||
|
||||
UdpPacketEncodingBuffer buffer;
|
||||
{
|
||||
buffer.Resize(buffer.GetCapacity());
|
||||
@@ -457,11 +492,12 @@ namespace AzNetworking
|
||||
uint32_t packetSize = buffer.GetSize();
|
||||
uint8_t* packetData = buffer.GetBuffer();
|
||||
|
||||
// If the packet doesn't fit within our MTU, break it up
|
||||
if (packetSize > connection.GetConnectionMtu())
|
||||
// If the packet doesn't fit within our MTU (minus potential SSL encryption overhead), break it up
|
||||
if (packetSize > connection.GetConnectionMtu() - net_SslInflationOverhead)
|
||||
{
|
||||
// Each fragmented packet we send adds an extra fragmented packet header, need to deduct that from our chunk size, otherwise we infinitely loop
|
||||
const uint32_t chunkSize = connection.GetConnectionMtu() - net_FragmentedHeaderOverhead;
|
||||
// SSL encryption can also inflate our payload so we pre-emptively deduct an estimated tax
|
||||
const uint32_t chunkSize = connection.GetConnectionMtu() - net_FragmentedHeaderOverhead - net_SslInflationOverhead;
|
||||
const uint32_t numChunks = (packetSize + chunkSize - 1) / chunkSize; // We want to round up on the remainder
|
||||
const uint8_t* chunkStart = packetData;
|
||||
const SequenceId fragmentedSequence = connection.m_fragmentQueue.GetNextFragmentedSequenceId();
|
||||
@@ -529,30 +565,9 @@ namespace AzNetworking
|
||||
aznumeric_cast<uint32_t>(header.GetSequenceWindow())
|
||||
);
|
||||
|
||||
// If it's a reliable packet, make sure our reliable queue knows about it now because we might need to drop it if our connection is not set up
|
||||
if (reliabilityType == ReliabilityType::Reliable)
|
||||
{
|
||||
if (!connection.PrepareReliablePacketForSend(localPacketId, reliableSequence, packet))
|
||||
{
|
||||
connection.Disconnect(DisconnectReason::ReliableQueueFull, TerminationEndpoint::Local);
|
||||
}
|
||||
}
|
||||
|
||||
// Okay, if we're still connecting, and the packet we're trying to send is not a retransmitted initiate connection packet, return an error, don't send yet
|
||||
if (connection.GetDtlsEndpoint().IsConnecting() && (packet.GetPacketType() != aznumeric_cast<PacketType>(CorePackets::PacketType::InitiateConnectionPacket)))
|
||||
{
|
||||
const DtlsEndpoint::ConnectResult result = connection.CompleteHandshake();
|
||||
if (result != DtlsEndpoint::ConnectResult::Complete) // DTLS handshake still in progress
|
||||
{
|
||||
// IMPORTANT that we register with the timeout queue here, otherwise we don't have the timer to pop for reliable packets
|
||||
RegisterWithTimeoutQueue(connection.GetConnectionId(), localPacketId, reliabilityType, connection.GetMetrics());
|
||||
AZLOG(NET_DebugDtls, "Connection is still in handshake negotiation, blocking packet send for packet type %d", (int)packet.GetPacketType());
|
||||
return localPacketId;
|
||||
}
|
||||
}
|
||||
|
||||
AZLOG(NET_DebugDtls, "Connection is sending packet type %d", aznumeric_cast<int32_t>(packet.GetPacketType()));
|
||||
const bool shouldEncrypt = packet.GetPacketType() == aznumeric_cast<PacketType>(CorePackets::PacketType::InitiateConnectionPacket);
|
||||
// If we're not connected then we're still handshaking and require packets to be unencrypted
|
||||
const bool shouldEncrypt = !IsHandshakePacket(connection.GetDtlsEndpoint(), packet.GetPacketType());
|
||||
if (m_socket->Send(address, packetData, packetSize, shouldEncrypt, connection.GetDtlsEndpoint(), connection.GetConnectionQuality()))
|
||||
{
|
||||
RegisterWithTimeoutQueue(connection.GetConnectionId(), localPacketId, reliabilityType, connection.GetMetrics());
|
||||
@@ -626,11 +641,10 @@ namespace AzNetworking
|
||||
|
||||
AZLOG(Debug_UdpConnect, "Accepted new Udp Connection");
|
||||
AZStd::unique_ptr<UdpConnection> connection = AZStd::make_unique<UdpConnection>(connectionId, connectPacket.m_address, *this, ConnectionRole::Acceptor);
|
||||
UdpPacketEncodingBuffer dtlsData;
|
||||
m_socket->AcceptDtlsEndpoint(connection->GetDtlsEndpoint(), connectPacket.m_address, dtlsData);
|
||||
DtlsEndpoint::ConnectResult result = m_socket->AcceptDtlsEndpoint(connection->GetDtlsEndpoint(), connectPacket.m_address);
|
||||
|
||||
// We're accepting this connection, so we can immediately transition to a connected state
|
||||
connection->m_state = ConnectionState::Connected;
|
||||
// Transition state based on our how our socket resolved
|
||||
connection->m_state = result == DtlsEndpoint::ConnectResult::Complete ? ConnectionState::Connected : ConnectionState::Connecting;
|
||||
connection->SetTimeoutId(timeoutId);
|
||||
m_connectionListener.OnConnect(connection.get());
|
||||
m_connectionSet.AddConnection(AZStd::move(connection));
|
||||
@@ -646,6 +660,14 @@ namespace AzNetworking
|
||||
m_removedConnections.emplace_back(RemovedConnection{ connection, reason, endpoint });
|
||||
}
|
||||
|
||||
bool UdpNetworkInterface::IsHandshakePacket(const DtlsEndpoint& endpoint, PacketType packetType) const
|
||||
{
|
||||
// Packets involved in handshake are InitiateConnection, ConnectionHandshake and FragmentedPackets of ConnectionHandshake
|
||||
return packetType == aznumeric_cast<PacketType>(CorePackets::PacketType::InitiateConnectionPacket) ||
|
||||
packetType == aznumeric_cast<PacketType>(CorePackets::PacketType::ConnectionHandshakePacket) ||
|
||||
(packetType == aznumeric_cast<PacketType>(CorePackets::PacketType::FragmentedPacket) && endpoint.IsConnecting());
|
||||
}
|
||||
|
||||
|
||||
UdpNetworkInterface::ConnectionTimeoutFunctor::ConnectionTimeoutFunctor(UdpNetworkInterface& networkInterface)
|
||||
: m_networkInterface(networkInterface)
|
||||
@@ -668,7 +690,9 @@ namespace AzNetworking
|
||||
{
|
||||
if (udpConnection->GetDtlsEndpoint().IsConnecting())
|
||||
{
|
||||
udpConnection->CompleteHandshake();
|
||||
// DTLS prefers we resend data lost over the wire with fresh SSL IDs so account for that here
|
||||
UdpPacketEncodingBuffer dtlsData;
|
||||
udpConnection->ProcessHandshakeData(dtlsData);
|
||||
return TimeoutResult::Refresh;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,12 @@ namespace AzNetworking
|
||||
//! @param endpoint whether the disconnection was initiated locally or remotely
|
||||
void RequestDisconnect(UdpConnection* connection, DisconnectReason reason, TerminationEndpoint endpoint);
|
||||
|
||||
//! Internal helper to check if a packet's type is for connection handshake
|
||||
//! @param endpoint DTLS endpoint participating in the handshake
|
||||
//! @param packetType type of the packet
|
||||
//! @return if the packet is for handshake
|
||||
bool IsHandshakePacket(const DtlsEndpoint& endpoint, AzNetworking::PacketType packetType) const;
|
||||
|
||||
AZ_DISABLE_COPY_MOVE(UdpNetworkInterface);
|
||||
|
||||
struct ConnectionTimeoutFunctor final
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/EBus/IEventScheduler.h>
|
||||
#include <AzCore/EBus/ScheduledEvent.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
|
||||
namespace AzNetworking
|
||||
{
|
||||
@@ -41,13 +44,8 @@ namespace AzNetworking
|
||||
return DtlsEndpoint::ConnectResult::Complete;
|
||||
}
|
||||
|
||||
DtlsEndpoint::ConnectResult UdpSocket::AcceptDtlsEndpoint(DtlsEndpoint&, const IpAddress&, const UdpPacketEncodingBuffer& dtlsData) const
|
||||
DtlsEndpoint::ConnectResult UdpSocket::AcceptDtlsEndpoint(DtlsEndpoint&, const IpAddress&) const
|
||||
{
|
||||
if (dtlsData.GetSize() > 0)
|
||||
{
|
||||
AZLOG_WARN("Encryption is disabled on accepting endpoint, but connector provided a DTLS handshake blob. Check that encryption is properly disabled on *BOTH* endpoints");
|
||||
return DtlsEndpoint::ConnectResult::Failed;
|
||||
}
|
||||
// No-op, no encryption wrapper required
|
||||
return DtlsEndpoint::ConnectResult::Complete;
|
||||
}
|
||||
@@ -108,28 +106,6 @@ namespace AzNetworking
|
||||
m_socketFd = InvalidSocketFd;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_LATENCY_DEBUG
|
||||
//! Checks packets deferred by latency debug and sends any that have passed their latency threshold
|
||||
void UdpSocket::ProcessDeferredPackets()
|
||||
{
|
||||
const AZ::TimeMs currTimeMs = AZ::GetElapsedTimeMs();
|
||||
while (m_sendBuffer.size() > 0)
|
||||
{
|
||||
const DeferredData& sendData = m_sendBuffer.front();
|
||||
if (sendData.m_timeDeferredMs >= currTimeMs)
|
||||
{
|
||||
SendInternal(sendData.m_address, sendData.m_dataBuffer.GetBuffer(), sendData.m_dataBuffer.GetSize(), sendData.m_encrypt, *sendData.m_dtlsEndpoint);
|
||||
m_sendBuffer.pop_front();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send buffer is sorted on append so all subsequent elements should also have time remaining
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int32_t UdpSocket::Send
|
||||
(
|
||||
const IpAddress& address,
|
||||
@@ -146,17 +122,6 @@ namespace AzNetworking
|
||||
AZ_Assert(address.GetAddress(ByteOrder::Host) != 0, "Invalid address");
|
||||
AZ_Assert(address.GetPort(ByteOrder::Host) != 0, "Invalid address");
|
||||
|
||||
#ifdef ENABLE_LATENCY_DEBUG
|
||||
if ((connectionQuality.m_latencyMs > AZ::TimeMs{ 0 }) || (connectionQuality.m_varianceMs > AZ::TimeMs{ 0 }))
|
||||
{
|
||||
const AZ::TimeMs jitterMs = aznumeric_cast<AZ::TimeMs>(m_random.GetRandom()) % (connectionQuality.m_varianceMs / aznumeric_cast<AZ::TimeMs>(2));
|
||||
const AZ::TimeMs currTimeMs = AZ::GetElapsedTimeMs();
|
||||
const AZ::TimeMs deferTimeMs = (connectionQuality.m_latencyMs / aznumeric_cast<AZ::TimeMs>(2)) + jitterMs;
|
||||
m_sendBuffer.push_back(DeferredData(currTimeMs + deferTimeMs, address, data, size, encrypt, dtlsEndpoint));
|
||||
std::sort(m_sendBuffer.begin(), m_sendBuffer.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!IsOpen())
|
||||
{
|
||||
return 0;
|
||||
@@ -193,6 +158,18 @@ namespace AzNetworking
|
||||
AZLOG_ERROR("Failed to write to socket (%d:%s)", error, GetNetworkErrorDesc(error));
|
||||
}
|
||||
}
|
||||
#ifdef ENABLE_LATENCY_DEBUG
|
||||
else if ((connectionQuality.m_latencyMs > AZ::TimeMs{ 0 }) || (connectionQuality.m_varianceMs > AZ::TimeMs{ 0 }))
|
||||
{
|
||||
const AZ::TimeMs jitterMs = aznumeric_cast<AZ::TimeMs>(m_random.GetRandom()) % (connectionQuality.m_varianceMs / aznumeric_cast<AZ::TimeMs>(2));
|
||||
const AZ::TimeMs currTimeMs = AZ::GetElapsedTimeMs();
|
||||
const AZ::TimeMs deferTimeMs = (connectionQuality.m_latencyMs / aznumeric_cast<AZ::TimeMs>(2)) + jitterMs;
|
||||
|
||||
DeferredData deferred = DeferredData(address, data, size, encrypt, dtlsEndpoint);
|
||||
AZ::Interface<AZ::IEventScheduler>::Get()->AddCallback([&, deferredData = deferred]
|
||||
{ SendInternalDeferred(deferredData); }, AZ::Name("Deferred packet"), deferTimeMs);
|
||||
}
|
||||
#endif
|
||||
|
||||
m_sentPackets++;
|
||||
m_sentBytes += sentBytes;
|
||||
@@ -253,7 +230,7 @@ namespace AzNetworking
|
||||
}
|
||||
|
||||
int32_t UdpSocket::SendInternal(const IpAddress& address, const uint8_t* data, uint32_t size,
|
||||
[[maybe_unused]] bool dontEncypt, [[maybe_unused]] DtlsEndpoint& dtlsEndpoint) const
|
||||
[[maybe_unused]] bool encrypt, [[maybe_unused]] DtlsEndpoint& dtlsEndpoint) const
|
||||
{
|
||||
sockaddr_in destAddr;
|
||||
memset(&destAddr, 0, sizeof(destAddr));
|
||||
@@ -262,4 +239,11 @@ namespace AzNetworking
|
||||
destAddr.sin_port = address.GetPort(ByteOrder::Network);
|
||||
return sendto(static_cast<int32_t>(m_socketFd), reinterpret_cast<const char*>(data), size, 0, (sockaddr*)&destAddr, sizeof(destAddr));
|
||||
}
|
||||
|
||||
#ifdef ENABLE_LATENCY_DEBUG
|
||||
int32_t UdpSocket::SendInternalDeferred(const DeferredData& data) const
|
||||
{
|
||||
return SendInternal(data.m_address, data.m_dataBuffer.GetBuffer(), data.m_dataBuffer.GetSize(), data.m_encrypt, *data.m_dtlsEndpoint);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -23,6 +23,12 @@
|
||||
# define ENABLE_LATENCY_DEBUG 1
|
||||
#endif
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
// Forwards
|
||||
class ScheduledEvent;
|
||||
}
|
||||
|
||||
namespace AzNetworking
|
||||
{
|
||||
// Forwards
|
||||
@@ -57,9 +63,8 @@ namespace AzNetworking
|
||||
//! Accepts an encryption socket wrapper.
|
||||
//! @param dtlsEndpoint the encryption wrapper instance to create a connection over
|
||||
//! @param address the IP address of the endpoint to connect to
|
||||
//! @param dtlsData data buffer containing the dtls handshake packet
|
||||
//! @return a connect result specifying whether the connection is still pending, failed, or complete
|
||||
virtual DtlsEndpoint::ConnectResult AcceptDtlsEndpoint(DtlsEndpoint& dtlsEndpoint, const IpAddress& address, const UdpPacketEncodingBuffer& dtlsData) const;
|
||||
virtual DtlsEndpoint::ConnectResult AcceptDtlsEndpoint(DtlsEndpoint& dtlsEndpoint, const IpAddress& address) const;
|
||||
|
||||
//! Opens the UDP socket on the given port.
|
||||
//! @param port the port number to open the UDP socket on, 0 will bind to any available port
|
||||
@@ -71,11 +76,6 @@ namespace AzNetworking
|
||||
//! Closes an open socket.
|
||||
virtual void Close();
|
||||
|
||||
#ifdef ENABLE_LATENCY_DEBUG
|
||||
//! Checks packets deferred by latency debug and sends any that have passed their latency threshold
|
||||
virtual void ProcessDeferredPackets();
|
||||
#endif
|
||||
|
||||
//! Returns true if the UDP socket is currently in an open state.
|
||||
//! @return boolean true if the socket is in a connected state
|
||||
bool IsOpen() const;
|
||||
@@ -105,12 +105,20 @@ namespace AzNetworking
|
||||
//! @return the total number of packets sent on this socket
|
||||
uint32_t GetSentPackets() const;
|
||||
|
||||
//! Returns the total number of bytes received on this socket.
|
||||
//! @return the total number of bytes received on this socket
|
||||
//! Returns the total number of bytes sent on this socket.
|
||||
//! @return the total number of bytes sent on this socket
|
||||
uint32_t GetSentBytes() const;
|
||||
|
||||
//! Returns the total number of packets sent on this socket.
|
||||
//! @return the total number of packets sent on this socket
|
||||
//! Returns the total number of encrypted packets sent on this socket.
|
||||
//! @return the total number of encrypted packets sent on this socket
|
||||
uint32_t GetSentPacketsEncrypted() const;
|
||||
|
||||
//! Returns the total number of additional bytes sent on this socket due to SSL encryption.
|
||||
//! @return the total number of additional bytes sent on this socket due to SSL encryption
|
||||
uint32_t GetSentBytesEncryptionInflation() const;
|
||||
|
||||
//! Returns the total number of packets received on this socket.
|
||||
//! @return the total number of packets received on this socket
|
||||
uint32_t GetRecvPackets() const;
|
||||
|
||||
//! Returns the total number of bytes received on this socket.
|
||||
@@ -119,6 +127,9 @@ namespace AzNetworking
|
||||
|
||||
protected:
|
||||
|
||||
mutable uint32_t m_sentPacketsEncrypted = 0;
|
||||
mutable uint32_t m_sentBytesEncryptionInflation = 0;
|
||||
|
||||
virtual int32_t SendInternal(const IpAddress& address, const uint8_t* data, uint32_t size, bool encrypt, DtlsEndpoint& dtlsEndpoint) const;
|
||||
|
||||
private:
|
||||
@@ -132,29 +143,24 @@ namespace AzNetworking
|
||||
#ifdef ENABLE_LATENCY_DEBUG
|
||||
struct DeferredData
|
||||
{
|
||||
DeferredData(AZ::TimeMs timeDeferredMs, const IpAddress& address, const uint8_t* data, uint32_t size, bool encrypt, DtlsEndpoint& dtlsEndpoint)
|
||||
: m_timeDeferredMs(timeDeferredMs)
|
||||
, m_address(address)
|
||||
DeferredData(const IpAddress& address, const uint8_t* data, uint32_t size, bool encrypt, DtlsEndpoint& dtlsEndpoint)
|
||||
: m_address(address)
|
||||
, m_encrypt(encrypt)
|
||||
, m_dtlsEndpoint(&dtlsEndpoint)
|
||||
{
|
||||
m_dataBuffer.CopyValues(data, size);
|
||||
}
|
||||
|
||||
bool operator <(const DeferredData& rhs) const
|
||||
{
|
||||
return m_timeDeferredMs < rhs.m_timeDeferredMs;
|
||||
}
|
||||
|
||||
AZ::TimeMs m_timeDeferredMs;
|
||||
IpAddress m_address;
|
||||
bool m_encrypt;
|
||||
DtlsEndpoint* m_dtlsEndpoint;
|
||||
UdpPacketEncodingBuffer m_dataBuffer;
|
||||
DtlsEndpoint* m_dtlsEndpoint = nullptr;
|
||||
AZ::ScheduledEvent* m_owningEvent = nullptr;
|
||||
IpAddress m_address;
|
||||
// Deferred UDP packets should have gone through UDP Fragmentation already so ChunkBuffer is sufficient size
|
||||
ChunkBuffer m_dataBuffer;
|
||||
};
|
||||
|
||||
mutable AZStd::deque<DeferredData> m_sendBuffer;
|
||||
mutable AZStd::deque<DeferredData> m_recvBuffer;
|
||||
int32_t SendInternalDeferred(const DeferredData& data) const;
|
||||
|
||||
mutable AZ::SimpleLcgRandom m_random;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -34,6 +34,16 @@ namespace AzNetworking
|
||||
return m_sentBytes;
|
||||
}
|
||||
|
||||
inline uint32_t UdpSocket::GetSentPacketsEncrypted() const
|
||||
{
|
||||
return m_sentPacketsEncrypted;
|
||||
}
|
||||
|
||||
inline uint32_t UdpSocket::GetSentBytesEncryptionInflation() const
|
||||
{
|
||||
return m_sentBytesEncryptionInflation;
|
||||
}
|
||||
|
||||
inline uint32_t UdpSocket::GetRecvPackets() const
|
||||
{
|
||||
return m_recvPackets;
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Console/IConsole.h>
|
||||
#include <AzNetworking/Framework/ICompressor.h>
|
||||
#include <AzNetworking/Utilities/CompressionCommon.h>
|
||||
|
||||
#ifdef ENABLE_MULTIPLAYER_COMPRESSION
|
||||
// Requires the MultiplayerCompression Gem be enabled and set as a dependency of the Multiplayer Gem
|
||||
#include <MultiplayerCompression/MultiplayerCompressionBus.h>
|
||||
#endif
|
||||
|
||||
namespace AzNetworking
|
||||
{
|
||||
AZStd::unique_ptr<ICompressor> CreateCompressor(AZStd::string_view compressorName)
|
||||
{
|
||||
CompressorType compressorType = aznumeric_cast<AzNetworking::CompressorType>(static_cast<AZ::u32>(AZ::Crc32(compressorName)));
|
||||
|
||||
#ifdef ENABLE_MULTIPLAYER_COMPRESSION
|
||||
// Requires the MultiplayerCompression Gem be enabled and set as a dependency of the Multiplayer Gem
|
||||
CompressorType lz4Type;
|
||||
MultiplayerCompression::MultiplayerCompressionRequestBus::BroadcastResult(lz4Type, &MultiplayerCompression::MultiplayerCompressionRequests::GetType);
|
||||
if (lz4Type == compressorType)
|
||||
{
|
||||
AZStd::shared_ptr<ICompressorFactory> compressorFactory;
|
||||
MultiplayerCompression::MultiplayerCompressionRequestBus::BroadcastResult(compressorFactory, &MultiplayerCompression::MultiplayerCompressionRequests::GetCompressionFactory);
|
||||
return compressorFactory->Create();
|
||||
}
|
||||
#endif
|
||||
|
||||
AZ_Warning("CompressionCommon", false, "No compressor was found matching %.*s, check that related Gems are enabled.",
|
||||
aznumeric_cast<int>(compressorName.size()), compressorName.data());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// forward declare
|
||||
class ICompressor;
|
||||
|
||||
// Common helper methods needed for both TLS and DTLS transport implementations
|
||||
namespace AzNetworking
|
||||
{
|
||||
//! Helper function to create a compressor, uses enabled Gems that supply compressors
|
||||
//! @param compressorName The string name of the compressor type to create, this is Crc32'd to select by CompressorType
|
||||
//! @return A unique_ptr to a Compressor implementation or nullptr on failure to match
|
||||
AZStd::unique_ptr<ICompressor> CreateCompressor(AZStd::string_view compressorName);
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzNetworking/Utilities/EncryptionCommon.h>
|
||||
#include <AzNetworking/Utilities/NetworkCommon.h>
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
namespace AzNetworking
|
||||
{
|
||||
AZ_CVAR(AZ::CVarFixedString, net_SslExternalCertificateFile, "testing.pem", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The filename of the EXTERNAL (server to client) certificate chain in PEM format (default is for debugging purposes)");
|
||||
AZ_CVAR(AZ::CVarFixedString, net_SslExternalCertificateFile, "testcert.pem", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The filename of the EXTERNAL (server to client) certificate chain in PEM format (default is for debugging purposes)");
|
||||
AZ_CVAR(AZ::CVarFixedString, net_SslExternalPrivateKeyFile, "testkey.pem", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The filename of the EXTERNAL (server to client) private key file in PEM format (default is for debugging purposes)");
|
||||
AZ_CVAR(AZ::CVarFixedString, net_SslExternalContextPassword, "12345", nullptr, AZ::ConsoleFunctorFlags::DontReplicate | AZ::ConsoleFunctorFlags::IsInvisible, "The password required for the EXTERNAL (server to client) private certificate (default is for debugging purposes)");
|
||||
|
||||
@@ -54,52 +54,53 @@ namespace AzNetworking
|
||||
void PrintSslErrorStack()
|
||||
{
|
||||
#if AZ_TRAIT_USE_OPENSSL
|
||||
const int32_t errorCode = ERR_get_error();
|
||||
const int32_t systemError = GetLastNetworkError();
|
||||
|
||||
switch (errorCode)
|
||||
{
|
||||
case SSL_ERROR_NONE:
|
||||
AZLOG_ERROR("%X - SSL_ERROR_NONE: last system error is (%d:%s)", errorCode, systemError, GetNetworkErrorDesc(systemError));
|
||||
break;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
AZLOG_ERROR("%X - SSL_ERROR_ZERO_RETURN: connection has been closed", errorCode);
|
||||
break;
|
||||
case SSL_ERROR_WANT_READ:
|
||||
AZLOG_ERROR("%X - SSL_ERROR_WANT_READ: socket is non-blocking, read buffer is empty", errorCode);
|
||||
break;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
AZLOG_ERROR("%X - SSL_ERROR_WANT_WRITE: socket is non-blocking, write buffer is full", errorCode);
|
||||
break;
|
||||
case SSL_ERROR_WANT_CONNECT:
|
||||
AZLOG_ERROR("%X - SSL_ERROR_WANT_CONNECT: socket is non-blocking, connect failed and should be retried", errorCode);
|
||||
break;
|
||||
case SSL_ERROR_WANT_ACCEPT:
|
||||
AZLOG_ERROR("%X - SSL_ERROR_WANT_ACCEPT: socket is non-blocking, accept failed and should be retried", errorCode);
|
||||
break;
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
const int32_t errorCode = ERR_get_error();
|
||||
const int32_t systemError = GetLastNetworkError();
|
||||
|
||||
switch (errorCode)
|
||||
{
|
||||
case SSL_ERROR_NONE:
|
||||
AZLOG_ERROR("%X - SSL_ERROR_NONE: last system error is (%d:%s)", errorCode, systemError, GetNetworkErrorDesc(systemError));
|
||||
break;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
AZLOG_ERROR("%X - SSL_ERROR_ZERO_RETURN: connection has been closed", errorCode);
|
||||
break;
|
||||
case SSL_ERROR_WANT_READ:
|
||||
AZLOG_ERROR("%X - SSL_ERROR_WANT_READ: socket is non-blocking, read buffer is empty", errorCode);
|
||||
break;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
AZLOG_ERROR("%X - SSL_ERROR_WANT_WRITE: socket is non-blocking, write buffer is full", errorCode);
|
||||
break;
|
||||
case SSL_ERROR_WANT_CONNECT:
|
||||
AZLOG_ERROR("%X - SSL_ERROR_WANT_CONNECT: socket is non-blocking, connect failed and should be retried", errorCode);
|
||||
break;
|
||||
case SSL_ERROR_WANT_ACCEPT:
|
||||
AZLOG_ERROR("%X - SSL_ERROR_WANT_ACCEPT: socket is non-blocking, accept failed and should be retried", errorCode);
|
||||
break;
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
AZLOG_ERROR("%X - SSL_ERROR_WANT_X509_LOOKUP: operation did not complete, SSL_CTX_set_client_cert_cb() has asked to be called again, operation should be retried", errorCode);
|
||||
break;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
break;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
AZLOG_ERROR("%X - SSL_ERROR_SYSCALL: system error, check errno (%d:%s)", errorCode, systemError, GetNetworkErrorDesc(systemError));
|
||||
break;
|
||||
case SSL_ERROR_SSL:
|
||||
break;
|
||||
case SSL_ERROR_SSL:
|
||||
AZLOG_ERROR("%X - SSL_ERROR_SSL: lib %s, func %s, reason %s", errorCode, ERR_lib_error_string(errorCode), ERR_func_error_string(errorCode), ERR_reason_error_string(errorCode));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
AZLOG_ERROR("%X - Unknown error code: lib %s, func %s, reason %s", errorCode, ERR_lib_error_string(errorCode), ERR_func_error_string(errorCode), ERR_reason_error_string(errorCode));
|
||||
break;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_USE_OPENSSL
|
||||
static const uint32_t MaxCookieHistory = 8;
|
||||
static bool g_encryptionInitialized = false;
|
||||
static int32_t g_azNetworkingTrustDataIndex = 0;
|
||||
static AZ::TimeMs g_lastCookieTimestamp = AZ::TimeMs{ 0 };
|
||||
static uint64_t g_validCookieArray[MaxCookieHistory];
|
||||
static uint32_t g_cookieReplaceIndex = 0;
|
||||
static bool g_encryptionInitialized = false;
|
||||
static int32_t g_azNetworkingTrustDataIndex = 0;
|
||||
static AZ::TimeMs g_lastCookieTimestamp = AZ::TimeMs{0};
|
||||
static uint64_t g_validCookieArray[MaxCookieHistory];
|
||||
static uint32_t g_cookieReplaceIndex = 0;
|
||||
|
||||
static void GetCertificatePaths(TrustZone trustZone, AZStd::string& certificatePath, AZStd::string& privateKeyPath)
|
||||
{
|
||||
@@ -150,7 +151,7 @@ namespace AzNetworking
|
||||
AZStd::string unusedPrivateKeyPath;
|
||||
GetCertificatePaths(trustZone, certificatePath, unusedPrivateKeyPath);
|
||||
|
||||
AZ::IO::FileIOStream stream(certificatePath.c_str(), AZ::IO::OpenMode::ModeWrite);
|
||||
AZ::IO::FileIOStream stream(certificatePath.c_str(), AZ::IO::OpenMode::ModeRead);
|
||||
const AZ::IO::SizeType publicCertLength = stream.GetLength();
|
||||
if (publicCertLength <= 0)
|
||||
{
|
||||
@@ -245,7 +246,8 @@ namespace AzNetworking
|
||||
|
||||
// Catch a too long certificate chain. The depth limit set using SSL_CTX_set_verify_depth() is by purpose set to "limit+1" so that
|
||||
// whenever the "depth>verify_depth" condition is met, we have violated the limit and want to log this error condition.
|
||||
// We must do it here, because the CHAIN_TOO_LONG error would not be found explicitly; only errors introduced by cutting off the additional certificates would be logged.
|
||||
// We must do it here, because the CHAIN_TOO_LONG error would not be found explicitly; only errors introduced by cutting off the
|
||||
// additional certificates would be logged.
|
||||
const int32_t depth = X509_STORE_CTX_get_error_depth(context);
|
||||
if (depth > net_SslMaxCertDepth)
|
||||
{
|
||||
@@ -256,7 +258,7 @@ namespace AzNetworking
|
||||
// Validate certificate before and after times
|
||||
if (net_SslValidateExpiry)
|
||||
{
|
||||
const ASN1_TIME *notBeforeTime = X509_get_notBefore(err_cert);
|
||||
const ASN1_TIME* notBeforeTime = X509_get_notBefore(err_cert);
|
||||
const int32_t beforeTimeResult = X509_cmp_current_time(notBeforeTime);
|
||||
|
||||
if (beforeTimeResult >= 0)
|
||||
@@ -266,7 +268,7 @@ namespace AzNetworking
|
||||
result = OpenSslResultFailure;
|
||||
}
|
||||
|
||||
const ASN1_TIME *notAfterTime = X509_get_notAfter(err_cert);
|
||||
const ASN1_TIME* notAfterTime = X509_get_notAfter(err_cert);
|
||||
const int32_t afterTimeResult = X509_cmp_current_time(notAfterTime);
|
||||
|
||||
if (afterTimeResult <= 0)
|
||||
@@ -280,7 +282,7 @@ namespace AzNetworking
|
||||
if (net_SslEnablePinning)
|
||||
{
|
||||
SSL* sslSocket = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(context, SSL_get_ex_data_X509_STORE_CTX_idx()));
|
||||
SSL_CTX* sslContext = SSL_get_SSL_CTX(sslSocket);;
|
||||
SSL_CTX* sslContext = SSL_get_SSL_CTX(sslSocket);
|
||||
TrustZone trustZone = static_cast<TrustZone>(reinterpret_cast<uintptr_t>(SSL_CTX_get_ex_data(sslContext, g_azNetworkingTrustDataIndex)));
|
||||
if (!ValidatePinnedCertificate(err_cert, trustZone))
|
||||
{
|
||||
@@ -369,7 +371,6 @@ namespace AzNetworking
|
||||
{
|
||||
ERR_free_strings();
|
||||
EVP_cleanup();
|
||||
sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
|
||||
g_encryptionInitialized = false;
|
||||
@@ -486,10 +487,16 @@ namespace AzNetworking
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the clients certificate
|
||||
// Validate the clients certificate only on handshake
|
||||
SSL_CTX_set_verify(context, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, ValidateCertificateCallback);
|
||||
|
||||
// Set up ciphers using DH key exchange
|
||||
// Only support a single cipher suite in OpenSSL that supports:
|
||||
//
|
||||
// ECDHE Primary key exchange using ephemeral elliptic curve diffie-hellman.
|
||||
// RSA Authentication (public and private key) used to sign ECDHE parameters and can be checked against a CA.
|
||||
// AES256 AES cipher for symmetric key encryption using a 256-bit key.
|
||||
// GCM Mode of operation for symmetric key encryption.
|
||||
// SHA384 SHA-2 hashing algorithm.
|
||||
const AZ::CVarFixedString ciphers = net_SslCertCiphers;
|
||||
if (SSL_CTX_set_cipher_list(context, ciphers.c_str()) != OpenSslResultSuccess)
|
||||
{
|
||||
@@ -501,6 +508,7 @@ namespace AzNetworking
|
||||
SSL_CTX_set_cookie_generate_cb(context, GenerateCookieCallback);
|
||||
SSL_CTX_set_cookie_verify_cb(context, VerifyCookieCallback);
|
||||
|
||||
// Automatically generate parameters for elliptic-curve diffie-hellman (i.e. curve type and coefficients).
|
||||
SSL_CTX_set_ecdh_auto(context, 1);
|
||||
|
||||
scopedFree.ReleaseSslContextWithoutFree();
|
||||
@@ -585,9 +593,9 @@ namespace AzNetworking
|
||||
|
||||
// SSL_shutdown can do very bad things if the SSL context is in a bad state
|
||||
// Further, the documentation around safely using SSL_shutdown is extremely confusing and doesn't provide a functional example
|
||||
// We never terminate encryption on a connection to send further data in plain-text, we explicitly close TCP sockets and UDP virtual connections are deleted
|
||||
// Therefore just removing the call to SSL_shutdown for now
|
||||
//SSL_shutdown(sslSocket);
|
||||
// We never terminate encryption on a connection to send further data in plain-text, we explicitly close TCP sockets and UDP virtual
|
||||
// connections are deleted Therefore just removing the call to SSL_shutdown for now
|
||||
// SSL_shutdown(sslSocket);
|
||||
SSL_free(sslSocket);
|
||||
sslSocket = nullptr;
|
||||
#endif
|
||||
@@ -620,4 +628,4 @@ namespace AzNetworking
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} // namespace AzNetworking
|
||||
|
||||
@@ -124,8 +124,6 @@ set(FILES
|
||||
UdpTransport/UdpSocket.inl
|
||||
Utilities/CidrAddress.cpp
|
||||
Utilities/CidrAddress.h
|
||||
Utilities/CompressionCommon.cpp
|
||||
Utilities/CompressionCommon.h
|
||||
Utilities/EncryptionCommon.cpp
|
||||
Utilities/EncryptionCommon.h
|
||||
Utilities/Endian.h
|
||||
|
||||
@@ -40,16 +40,6 @@ ly_add_target(
|
||||
*.AutoPackets.xml,AutoPackets_Source.jinja,$path/$fileprefix.AutoPackets.cpp
|
||||
)
|
||||
|
||||
# Add ENABLE_MULTIPLAYER_COMPRESSION define to the following source files when the LY_ENABLE_MULTIPLAYER_COMPRESSION cache variable has been set
|
||||
if($CACHE{LY_ENABLE_MULTIPLAYER_COMPRESSION})
|
||||
set_property(SOURCE
|
||||
CompressionCommon.cpp
|
||||
#...
|
||||
APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS ENABLE_MULTIPLAYER_COMPRESSION
|
||||
)
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
# Tests
|
||||
################################################################################
|
||||
|
||||
Reference in New Issue
Block a user