From 1186c9367298c86a07669ae55196fe021830db8c Mon Sep 17 00:00:00 2001 From: Segfault <5221072+Segfaultd@users.noreply.github.com> Date: Sun, 18 Oct 2026 10:55:13 +0200 Subject: [PATCH 1/8] feat(voice): relay voice frames on the network thread A relay host forwarded every voice frame from the application's Receive() loop, so any stall on the server's main thread was heard as voice breaking up, and worse the busier the server got. PluginInterface2 gains an opt-in OnReceiveNetworkThread() hook, called on RakPeer's update thread for each message from a connected system before it is queued for Receive(). RakVoice uses it through SetRelayFrameHandler(): a relay host with a handler takes ID_RAKVOICE_RELAY_DATA there and relays it without the application ever seeing it. Without a handler nothing changes. SimulatedNetwork::SetReceiveEnabled() lets tests stall a peer's Receive() while its update cycle keeps running. --- Source/include/mafianet/peer.h | 6 + Source/include/mafianet/plugin_interface2.h | 16 ++ Source/include/mafianet/voice.h | 22 +- Source/src/peer.cpp | 43 +++- Source/src/voice.cpp | 28 ++- .../RakVoiceNetworkThreadRelayLiveTests.cpp | 125 ++++++++++ Tests/Support/SimulatedNetwork.cpp | 10 + Tests/Support/SimulatedNetwork.h | 7 + Tests/Unit/NetworkThreadReceiveTests.cpp | 221 ++++++++++++++++++ .../Unit/RakVoiceNetworkThreadRelayTests.cpp | 136 +++++++++++ 10 files changed, 611 insertions(+), 3 deletions(-) create mode 100644 Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp create mode 100644 Tests/Unit/NetworkThreadReceiveTests.cpp create mode 100644 Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp diff --git a/Source/include/mafianet/peer.h b/Source/include/mafianet/peer.h index 16408dd58..a3621952e 100644 --- a/Source/include/mafianet/peer.h +++ b/Source/include/mafianet/peer.h @@ -994,6 +994,10 @@ class RAK_DLL_EXPORT RakPeer : public RakPeerInterface, public RNS2EventHandler DataStructures::List banList; // Threadsafe, and not thread safe DataStructures::List pluginListTS, pluginListNTS; + // Plugins that opted into OnReceiveNetworkThread. Also in one of the lists above. The mutex + // lets AttachPlugin/DetachPlugin run while the update thread is dispatching. + DataStructures::List pluginListNetworkThread; + SimpleMutex pluginListNetworkThreadMutex; DataStructures::Queue requestedConnectionQueue; SimpleMutex requestedConnectionQueueMutex; @@ -1151,6 +1155,8 @@ class RAK_DLL_EXPORT RakPeer : public RakPeerInterface, public RNS2EventHandler void ResetSendReceipt(void); void OnConnectedPong(MafiaNet::Time sendPingTime, MafiaNet::Time sendPongTime, RemoteSystemStruct *remoteSystem); void CallPluginCallbacks(DataStructures::List &pluginList, Packet *packet); + // Offers a message to the network-thread plugins; true if one of them took it. + bool DispatchNetworkThreadReceive(Packet *packet); #if LIBCAT_SECURITY==1 // Encryption and security diff --git a/Source/include/mafianet/plugin_interface2.h b/Source/include/mafianet/plugin_interface2.h index 6a14a5854..5c293dfb8 100644 --- a/Source/include/mafianet/plugin_interface2.h +++ b/Source/include/mafianet/plugin_interface2.h @@ -139,6 +139,22 @@ class RAK_DLL_EXPORT PluginInterface2 /// If true, then you cannot call RakPeer::AttachPlugin() or RakPeer::DetachPlugin() for this plugin, while RakPeer is active virtual bool UsesReliabilityLayer(void) const {return false;} + /// Queried when attached to RakPeer + /// Return true to have OnReceiveNetworkThread() called. Queried once, at AttachPlugin(). + virtual bool UsesNetworkThreadReceive(void) const {return false;} + + /// Called on RakPeer's network update thread for each message from a connected system, before + /// it is queued for RakPeer::Receive(). Use it for traffic that must not wait for the + /// application to call Receive(), such as voice. Connection notifications are not routed here. + /// Runs concurrently with the user thread, so anything the plugin shares with its other + /// callbacks must be synchronized. RakPeer::Send() is safe to call from here; AttachPlugin() and + /// DetachPlugin() are not (they wait for this call to return). ID_TIMESTAMP payloads arrive unshifted. + /// \pre To be called, UsesNetworkThreadReceive() must return true + /// \param[in] packet The message. RR_STOP_PROCESSING_AND_DEALLOCATE frees it, RR_STOP_PROCESSING + /// hands it to the plugin (free it with RakPeer::DeallocatePacket), and + /// RR_CONTINUE_PROCESSING queues it for Receive() as usual. + virtual PluginReceiveResult OnReceiveNetworkThread(Packet *packet) {(void) packet; return RR_CONTINUE_PROCESSING;} + /// Called on a send to the socket, per datagram, that does not go through the reliability layer /// \pre To be called, UsesReliabilityLayer() must return true /// \param[in] data The data being sent diff --git a/Source/include/mafianet/voice.h b/Source/include/mafianet/voice.h index f8e909319..e4e887708 100644 --- a/Source/include/mafianet/voice.h +++ b/Source/include/mafianet/voice.h @@ -24,6 +24,10 @@ #include "mafianet/plugin_interface2.h" #include "mafianet/ds_ordered_list.h" #include "mafianet/native_types.h" +#include "mafianet/simple_mutex.h" + +#include +#include // Forward declarations for Opus and RNNoise struct OpusEncoder; @@ -312,6 +316,17 @@ class RAK_DLL_EXPORT RakVoice : public PluginInterface2 /// Distinct from SetRelayMode(), which is what a talking/listening client enables. void SetRelayHost(bool enable); + /// rief Relays frames on RakPeer's network thread instead of the application loop. + /// With a handler set, a relay host hands each incoming ID_RAKVOICE_RELAY_DATA packet to + ///  handler on the network update thread and frees it afterwards, so the frame never + /// reaches Receive() and is forwarded even while the application's main thread is busy. + /// The handler typically picks recipients and calls RelayFrame(), which is safe there; it must + /// not keep the packet, and whatever it reads must be safe to read from that thread. + /// Pass nullptr to go back to passing frames through to Receive(). Once this returns, the + /// previous handler is not running and will not be called again, so do not call this from + /// inside the handler. Ignored unless relay host. + void SetRelayFrameHandler(std::function handler); + /// \brief Forwards a received relay frame to a set of recipients without decoding it. /// Costs one Send per recipient; no codec state is touched. void RelayFrame(Packet *packet, const RakNetGUID *recipients, int count); @@ -352,6 +367,8 @@ class RAK_DLL_EXPORT RakVoice : public PluginInterface2 virtual void OnShutdown(void); virtual void Update(void); virtual PluginReceiveResult OnReceive(Packet *packet); + virtual bool UsesNetworkThreadReceive(void) const { return true; } + virtual PluginReceiveResult OnReceiveNetworkThread(Packet *packet); virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); protected: @@ -405,7 +422,10 @@ class RAK_DLL_EXPORT RakVoice : public PluginInterface2 int defaultSignalType; bool loopbackMode; bool relayMode; - bool relayHost; + // Read on the network thread by OnReceiveNetworkThread. + std::atomic relayHost; + std::function relayFrameHandler; + MafiaNet::SimpleMutex relayFrameHandlerMutex; bool perSpeakerOutput; /// Concurrent relay decoders the application allows; 0 means only the memory backstop. unsigned maxDecodedSpeakers; diff --git a/Source/src/peer.cpp b/Source/src/peer.cpp index 47e49a2be..9700ca210 100644 --- a/Source/src/peer.cpp +++ b/Source/src/peer.cpp @@ -2929,6 +2929,14 @@ void RakPeer::AttachPlugin( PluginInterface2 *plugin ) pluginListTS.Insert(plugin, _FILE_AND_LINE_); } } + + if (plugin->UsesNetworkThreadReceive()) + { + pluginListNetworkThreadMutex.Lock(); + if (pluginListNetworkThread.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) + pluginListNetworkThread.Insert(plugin, _FILE_AND_LINE_); + pluginListNetworkThreadMutex.Unlock(); + } } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -2964,6 +2972,17 @@ void RakPeer::DetachPlugin( PluginInterface2 *plugin ) pluginListTS.RemoveFromEnd(); } } + // Removed under the lock the update thread dispatches under, so once this returns the plugin + // is not inside OnReceiveNetworkThread and will not be called again. + pluginListNetworkThreadMutex.Lock(); + index = pluginListNetworkThread.GetIndexOf(plugin); + if (index!=MAX_UNSIGNED_LONG) + { + pluginListNetworkThread[index]=pluginListNetworkThread[pluginListNetworkThread.Size()-1]; + pluginListNetworkThread.RemoveFromEnd(); + } + pluginListNetworkThreadMutex.Unlock(); + plugin->OnDetach(); plugin->SetRakPeerInterface(0); } @@ -4835,6 +4854,27 @@ void RakPeer::ClearRequestedConnectionList(void) MafiaNet::OP_DELETE(freeQueue[i], _FILE_AND_LINE_ ); } } +bool RakPeer::DispatchNetworkThreadReceive(Packet *packet) +{ + pluginListNetworkThreadMutex.Lock(); + for (unsigned int i=0; i < pluginListNetworkThread.Size(); i++) + { + const PluginReceiveResult result = pluginListNetworkThread[i]->OnReceiveNetworkThread(packet); + if (result==RR_STOP_PROCESSING_AND_DEALLOCATE) + { + pluginListNetworkThreadMutex.Unlock(); + DeallocatePacket(packet); + return true; + } + if (result==RR_STOP_PROCESSING) + { + pluginListNetworkThreadMutex.Unlock(); + return true; + } + } + pluginListNetworkThreadMutex.Unlock(); + return false; +} inline void RakPeer::AddPacketToProducer(MafiaNet::Packet *p) { packetReturnMutex.Lock(); @@ -6935,7 +6975,8 @@ bool RakPeer::RunUpdateCycle(BitStream &updateBitStream ) packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; packet->guid = remoteSystem->guid; packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); + if (!DispatchNetworkThreadReceive(packet)) + AddPacketToProducer(packet); } else { diff --git a/Source/src/voice.cpp b/Source/src/voice.cpp index 41b7f7a55..6c50e098f 100644 --- a/Source/src/voice.cpp +++ b/Source/src/voice.cpp @@ -163,6 +163,15 @@ void RakVoice::SetRelayHost(bool enable) relayHost = enable; } +void RakVoice::SetRelayFrameHandler(std::function handler) +{ + // Swapped under the lock OnReceiveNetworkThread calls it under, so a handler being cleared is + // never mid-call when this returns. + relayFrameHandlerMutex.Lock(); + relayFrameHandler = std::move(handler); + relayFrameHandlerMutex.Unlock(); +} + bool RakVoice::SetOrderingChannels(char frameChannel, char controlChannel) { // char is unsigned on ARM; compare as signed so a negative channel is rejected @@ -743,7 +752,8 @@ PluginReceiveResult RakVoice::OnReceive(Packet *packet) case ID_RAKVOICE_RELAY_DATA: // A relay host never decodes. Plugin OnReceive runs inside RakPeer::Receive, before // the packet reaches the application loop, so swallowing it here would leave the - // server with nothing to hand to RelayFrame(). Pass it through instead. + // server with nothing to hand to RelayFrame(). Pass it through instead. With a relay + // frame handler set, OnReceiveNetworkThread has already taken it and it never gets here. if (relayHost) return RR_CONTINUE_PROCESSING; OnRelayVoiceData(packet); @@ -753,6 +763,22 @@ PluginReceiveResult RakVoice::OnReceive(Packet *packet) return RR_CONTINUE_PROCESSING; } +PluginReceiveResult RakVoice::OnReceiveNetworkThread(Packet *packet) +{ + if (!relayHost || packet == nullptr || packet->length == 0 || packet->data[0] != ID_RAKVOICE_RELAY_DATA) + return RR_CONTINUE_PROCESSING; + + relayFrameHandlerMutex.Lock(); + if (!relayFrameHandler) + { + relayFrameHandlerMutex.Unlock(); + return RR_CONTINUE_PROCESSING; + } + relayFrameHandler(packet); + relayFrameHandlerMutex.Unlock(); + return RR_STOP_PROCESSING_AND_DEALLOCATE; +} + void RakVoice::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason) { (void)systemAddress; diff --git a/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp b/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp new file mode 100644 index 000000000..398ac41b3 --- /dev/null +++ b/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2026, MafiaHub + * + * This source code is licensed under the MIT-style license found in the + * license.txt file in the root directory of this source tree. + */ + +// A relay host with RakVoice::SetRelayFrameHandler forwards voice on RakPeer's own +// update thread. The server here never calls Receive() at all -- the shape of an +// application whose main thread is stalled -- and the listener must still hear the +// talker. The unit suite proves the routing on the simulated network; this proves it +// on the real threaded update loop. + +#include + +#include "mafianet/get_time.h" +#include "mafianet/message_identifiers.h" +#include "mafianet/peer_handle.h" +#include "mafianet/sleep.h" +#include "mafianet/voice.h" + +#include +#include +#include + +using namespace MafiaNet; + +namespace +{ + std::vector RelayFrameFrom(RakNetGUID origin, unsigned short sequence) + { + std::vector f(RAKVOICE_RELAY_HEADER_SIZE + 16, 0x5A); + f[0] = ID_RAKVOICE_RELAY_DATA; + f[RAKVOICE_RELAY_OFFSET_VERSION] = RAKVOICE_RELAY_FORMAT_VERSION; + std::memcpy(f.data() + RAKVOICE_RELAY_OFFSET_ORIGIN, &origin.g, sizeof(uint64_t)); + const uint16_t channelId = 0; + std::memcpy(f.data() + RAKVOICE_RELAY_OFFSET_CHANNEL_ID, &channelId, sizeof(uint16_t)); + std::memcpy(f.data() + RAKVOICE_RELAY_OFFSET_SEQUENCE, &sequence, sizeof(unsigned short)); + return f; + } +} + +class RakVoiceNetworkThreadRelayLive : public ::testing::Test +{ + // Peers are RAII MafiaNet::Peer locals; the RakVoice is declared first so it + // outlives the server it is attached to. +}; + +TEST_F(RakVoiceNetworkThreadRelayLive, RelaysWhileTheServerNeverCallsReceive) +{ + RakVoice voice; + Peer server; + RakSleep(2); // distinct microsecond-seeded GUIDs on POSIX + Peer talker; + RakSleep(2); + Peer listener; + + SocketDescriptor serverSd(0, "127.0.0.1"); + ASSERT_EQ(server->Startup(2, &serverSd, 1), RAKNET_STARTED); + server->SetMaximumIncomingConnections(2); + const unsigned short serverPort = server->GetInternalID().GetPort(); + + std::atomic listenerGuid {UNASSIGNED_RAKNET_GUID.g}; + std::atomic handled {0}; + voice.SetRelayHost(true); + voice.SetRelayFrameHandler([&](Packet *packet) { + RakNetGUID to; + to.g = listenerGuid.load(); + voice.RelayFrame(packet, &to, 1); + ++handled; + }); + server->AttachPlugin(&voice); + + SocketDescriptor clientSd; + ASSERT_EQ(talker->Startup(1, &clientSd, 1), RAKNET_STARTED); + ASSERT_EQ(listener->Startup(1, &clientSd, 1), RAKNET_STARTED); + listenerGuid = listener->GetMyGUID().g; + ASSERT_EQ(talker->Connect("127.0.0.1", serverPort, 0, 0), CONNECTION_ATTEMPT_STARTED); + ASSERT_EQ(listener->Connect("127.0.0.1", serverPort, 0, 0), CONNECTION_ATTEMPT_STARTED); + + SystemAddress serverAddress; + serverAddress.SetBinaryAddress("127.0.0.1"); + serverAddress.SetPortHostOrder(serverPort); + + // The server is never pumped; the handshake completes on its update thread. Wait for + // both clients too: the server counts a connection before the client has seen it accepted. + auto allConnected = [&] { + return server->NumberOfConnections() == 2 && talker->GetConnectionState(serverAddress) == IS_CONNECTED && + listener->GetConnectionState(serverAddress) == IS_CONNECTED; + }; + TimeMS start = GetTimeMS(); + while (GetTimeMS() - start < 10000 && !allConnected()) + { + for (auto pkt : talker.incoming()) (void) pkt; + for (auto pkt : listener.incoming()) (void) pkt; + RakSleep(30); + } + ASSERT_TRUE(allConnected()); + + unsigned short sequence = 0; + bool heard = false; + start = GetTimeMS(); + while (GetTimeMS() - start < 10000 && !heard) + { + const std::vector frame = RelayFrameFrom(talker->GetMyGUID(), ++sequence); + talker->Send((const char *) frame.data(), (int) frame.size(), Priority::High, Reliability::Unreliable, 0, + serverAddress, false); + for (auto pkt : talker.incoming()) (void) pkt; + for (auto pkt : listener.incoming()) + { + if (pkt->length > 0 && pkt->data[0] == ID_RAKVOICE_RELAY_DATA) + { + heard = true; + EXPECT_EQ(talker->GetMyGUID(), RakVoice::ReadRelayOrigin(pkt.get())); + } + } + RakSleep(20); + } + EXPECT_TRUE(heard) << "listener never heard a frame relayed on the server's network thread"; + EXPECT_GT(handled.load(), 0); + + // Stop the handler before the voice it captures can go away. + voice.SetRelayFrameHandler(nullptr); + server->DetachPlugin(&voice); +} diff --git a/Tests/Support/SimulatedNetwork.cpp b/Tests/Support/SimulatedNetwork.cpp index 455716e93..ee9cac15d 100644 --- a/Tests/Support/SimulatedNetwork.cpp +++ b/Tests/Support/SimulatedNetwork.cpp @@ -232,6 +232,8 @@ namespace MafiaNet for (RakPeer *peer : peers) { peer->RunManualUpdate(); + if (receiveDisabled.count(peer) != 0) + continue; for (Packet *packet = peer->Receive(); packet; packet = peer->Receive()) { Message message; @@ -246,6 +248,14 @@ namespace MafiaNet } } + void SimulatedNetwork::SetReceiveEnabled(const RakPeer *peer, bool enabled) + { + if (enabled) + receiveDisabled.erase(peer); + else + receiveDisabled.insert(peer); + } + bool SimulatedNetwork::RunUntil(const std::function &pred, uint32_t maxSimMs, uint32_t stepMs) { for (uint32_t elapsed = 0; elapsed < maxSimMs; elapsed += stepMs) diff --git a/Tests/Support/SimulatedNetwork.h b/Tests/Support/SimulatedNetwork.h index d7a0868ce..5c26a1bd8 100644 --- a/Tests/Support/SimulatedNetwork.h +++ b/Tests/Support/SimulatedNetwork.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "mafianet/get_time.h" @@ -80,6 +81,11 @@ namespace MafiaNet // Step until pred() holds or maxSimMs of simulated time passed. bool RunUntil(const std::function &pred, uint32_t maxSimMs, uint32_t stepMs = 10); + // While disabled, Step() still runs the peer's update cycle (its "network + // thread") but never calls its Receive(): models an application whose main + // thread has stalled. Packets queue inside the peer until re-enabled. + void SetReceiveEnabled(const RakPeer *peer, bool enabled); + std::vector &Inbox(const RakPeer *peer); // First message with this id in peer's inbox, or null. const Message *Find(const RakPeer *peer, MessageID id) const; @@ -127,6 +133,7 @@ namespace MafiaNet std::vector peers; std::map > inboxes; + std::set receiveDisabled; std::map sockets; std::map, LinkRule> links; LinkRule defaultRule; diff --git a/Tests/Unit/NetworkThreadReceiveTests.cpp b/Tests/Unit/NetworkThreadReceiveTests.cpp new file mode 100644 index 000000000..7fa7d8fec --- /dev/null +++ b/Tests/Unit/NetworkThreadReceiveTests.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2026, MafiaHub + * + * This source code is licensed under the MIT-style license found in the + * license.txt file in the root directory of this source tree. + * + * PluginInterface2::OnReceiveNetworkThread: a plugin that opts in sees every + * message from a connected system inside the update cycle, before it is queued + * for RakPeer::Receive(). These run on the SimulatedNetwork with the server's + * Receive() switched off, which is what a stalled application main thread looks + * like to the peer: the update cycle keeps running, Receive() is never called. + */ + +#include + +#include + +#include "SimulatedNetwork.h" + +#include "mafianet/bit_stream.h" +#include "mafianet/message_identifiers.h" +#include "mafianet/plugin_interface2.h" + +using namespace MafiaNet; + +namespace +{ + const MessageID ID_NT_CONSUMED = ID_USER_PACKET_ENUM + 20; + const MessageID ID_NT_PASSED = ID_USER_PACKET_ENUM + 21; + const MessageID ID_NT_KEPT = ID_USER_PACKET_ENUM + 22; + const MessageID ID_NT_ECHO = ID_USER_PACKET_ENUM + 23; + + // Consumes ID_NT_CONSUMED, keeps ID_NT_KEPT (frees it later itself), echoes + // ID_NT_ECHO back to its sender, passes everything else through. + class NetworkThreadProbe : public PluginInterface2 + { + public: + explicit NetworkThreadProbe(bool optIn = true) : optIn(optIn) {} + + bool UsesNetworkThreadReceive(void) const override { return optIn; } + + PluginReceiveResult OnReceiveNetworkThread(Packet *packet) override + { + networkThreadIds.push_back(packet->data[0]); + switch (packet->data[0]) + { + case ID_NT_CONSUMED: + return RR_STOP_PROCESSING_AND_DEALLOCATE; + case ID_NT_KEPT: + kept.push_back(packet); + return RR_STOP_PROCESSING; + case ID_NT_ECHO: + GetRakPeerInterface()->Send((const char *) packet->data, (int) packet->length, Priority::High, + Reliability::Reliable, 0, packet->guid, false); + return RR_STOP_PROCESSING_AND_DEALLOCATE; + default: + return RR_CONTINUE_PROCESSING; + } + } + + PluginReceiveResult OnReceive(Packet *packet) override + { + userThreadIds.push_back(packet->data[0]); + return RR_CONTINUE_PROCESSING; + } + + size_t NetworkThreadCount(MessageID id) const { return Count(networkThreadIds, id); } + size_t UserThreadCount(MessageID id) const { return Count(userThreadIds, id); } + + std::vector kept; + + private: + static size_t Count(const std::vector &ids, MessageID id) + { + size_t n = 0; + for (MessageID seen : ids) + n += seen == id ? 1 : 0; + return n; + } + + bool optIn; + std::vector networkThreadIds; + std::vector userThreadIds; + }; + + void SendId(RakPeer *from, const SystemAddress &to, MessageID id) + { + BitStream bs; + bs.Write(id); + bs.Write((uint32_t) 0xC0FFEEu); + ASSERT_NE(0u, from->Send(&bs, Priority::High, Reliability::ReliableOrdered, 0, to, false)); + } + + size_t InboxCount(SimulatedNetwork &net, const RakPeer *peer, MessageID id) + { + size_t n = 0; + for (const auto &message : net.Inbox(peer)) + n += message.id == id ? 1 : 0; + return n; + } + + struct Pair + { + SimulatedNetwork net; + RakPeer *server = net.AddPeer(2, 2); + RakPeer *client = net.AddPeer(1); + + bool Start() { return net.Connect(client, server); } + }; +} + +TEST(NetworkThreadReceive, ConsumedMessageIsSeenWithoutReceiveAndNeverQueued) +{ + Pair p; + NetworkThreadProbe probe; + p.server->AttachPlugin(&probe); + ASSERT_TRUE(p.Start()); + + p.net.SetReceiveEnabled(p.server, false); + SendId(p.client, p.net.AddressOf(p.server), ID_NT_CONSUMED); + ASSERT_TRUE(p.net.RunUntil([&] { return probe.NetworkThreadCount(ID_NT_CONSUMED) == 1; }, 2000)); + + p.net.SetReceiveEnabled(p.server, true); + p.net.Step(50); + EXPECT_EQ(0u, InboxCount(p.net, p.server, ID_NT_CONSUMED)); + EXPECT_EQ(0u, probe.UserThreadCount(ID_NT_CONSUMED)); + p.server->DetachPlugin(&probe); +} + +TEST(NetworkThreadReceive, ContinuedMessageStillReachesReceiveAndOnReceive) +{ + Pair p; + NetworkThreadProbe probe; + p.server->AttachPlugin(&probe); + ASSERT_TRUE(p.Start()); + + p.net.SetReceiveEnabled(p.server, false); + SendId(p.client, p.net.AddressOf(p.server), ID_NT_PASSED); + ASSERT_TRUE(p.net.RunUntil([&] { return probe.NetworkThreadCount(ID_NT_PASSED) == 1; }, 2000)); + EXPECT_EQ(0u, InboxCount(p.net, p.server, ID_NT_PASSED)); + + p.net.SetReceiveEnabled(p.server, true); + ASSERT_TRUE(p.net.RunUntilReceived(p.server, ID_NT_PASSED, 2000)); + EXPECT_EQ(1u, InboxCount(p.net, p.server, ID_NT_PASSED)); + EXPECT_EQ(1u, probe.UserThreadCount(ID_NT_PASSED)); + EXPECT_EQ(1u, probe.NetworkThreadCount(ID_NT_PASSED)); + p.server->DetachPlugin(&probe); +} + +TEST(NetworkThreadReceive, StopProcessingHandsOwnershipToThePlugin) +{ + Pair p; + NetworkThreadProbe probe; + p.server->AttachPlugin(&probe); + ASSERT_TRUE(p.Start()); + + SendId(p.client, p.net.AddressOf(p.server), ID_NT_KEPT); + ASSERT_TRUE(p.net.RunUntil([&] { return probe.kept.size() == 1; }, 2000)); + p.net.Step(50); + EXPECT_EQ(0u, InboxCount(p.net, p.server, ID_NT_KEPT)); + + // Still valid: the peer did not free it. + Packet *kept = probe.kept.front(); + EXPECT_EQ(ID_NT_KEPT, kept->data[0]); + EXPECT_EQ(p.client->GetMyGUID(), kept->guid); + p.server->DeallocatePacket(kept); + p.server->DetachPlugin(&probe); +} + +TEST(NetworkThreadReceive, PluginCanReplyFromTheHookWhileReceiveIsStalled) +{ + Pair p; + NetworkThreadProbe probe; + p.server->AttachPlugin(&probe); + ASSERT_TRUE(p.Start()); + + p.net.SetReceiveEnabled(p.server, false); + SendId(p.client, p.net.AddressOf(p.server), ID_NT_ECHO); + EXPECT_TRUE(p.net.RunUntilReceived(p.client, ID_NT_ECHO, 2000)); + p.server->DetachPlugin(&probe); +} + +TEST(NetworkThreadReceive, PluginThatDoesNotOptInIsNeverCalledOnTheNetworkThread) +{ + Pair p; + NetworkThreadProbe probe(false); + p.server->AttachPlugin(&probe); + ASSERT_TRUE(p.Start()); + + SendId(p.client, p.net.AddressOf(p.server), ID_NT_CONSUMED); + ASSERT_TRUE(p.net.RunUntilReceived(p.server, ID_NT_CONSUMED, 2000)); + EXPECT_EQ(0u, probe.NetworkThreadCount(ID_NT_CONSUMED)); + EXPECT_EQ(1u, probe.UserThreadCount(ID_NT_CONSUMED)); + p.server->DetachPlugin(&probe); +} + +TEST(NetworkThreadReceive, DetachedPluginIsNoLongerCalled) +{ + Pair p; + NetworkThreadProbe probe; + p.server->AttachPlugin(&probe); + ASSERT_TRUE(p.Start()); + p.server->DetachPlugin(&probe); + + SendId(p.client, p.net.AddressOf(p.server), ID_NT_CONSUMED); + ASSERT_TRUE(p.net.RunUntilReceived(p.server, ID_NT_CONSUMED, 2000)); + EXPECT_EQ(0u, probe.NetworkThreadCount(ID_NT_CONSUMED)); +} + +TEST(NetworkThreadReceive, ConnectionEventsAreNotRoutedThroughTheHook) +{ + // Only application messages from connected systems go through the hook; + // lifecycle notifications stay on Receive() where plugins already expect them. + Pair p; + NetworkThreadProbe probe; + p.server->AttachPlugin(&probe); + ASSERT_TRUE(p.Start()); + EXPECT_EQ(0u, probe.NetworkThreadCount(ID_NEW_INCOMING_CONNECTION)); + EXPECT_EQ(1u, probe.UserThreadCount(ID_NEW_INCOMING_CONNECTION)); + p.server->DetachPlugin(&probe); +} diff --git a/Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp b/Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp new file mode 100644 index 000000000..d763abc74 --- /dev/null +++ b/Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2026, MafiaHub + * + * This source code is licensed under the MIT-style license found in the + * license.txt file in the root directory of this source tree. + * + * RakVoice::SetRelayFrameHandler: a relay host with a handler takes + * ID_RAKVOICE_RELAY_DATA on the network thread and forwards it there, so voice + * keeps flowing while the server application's main thread is stalled. The + * server's Receive() is switched off on the SimulatedNetwork to show it. + */ + +#include + +#include +#include +#include + +#include "SimulatedNetwork.h" + +#include "mafianet/message_identifiers.h" +#include "mafianet/voice.h" + +using namespace MafiaNet; + +namespace +{ + std::vector RelayFrameFrom(RakNetGUID origin, unsigned payloadBytes = 16) + { + std::vector f(RAKVOICE_RELAY_HEADER_SIZE + payloadBytes, 0x5A); + f[0] = ID_RAKVOICE_RELAY_DATA; + f[RAKVOICE_RELAY_OFFSET_VERSION] = RAKVOICE_RELAY_FORMAT_VERSION; + std::memcpy(f.data() + RAKVOICE_RELAY_OFFSET_ORIGIN, &origin.g, sizeof(uint64_t)); + const uint16_t channelId = 0; + std::memcpy(f.data() + RAKVOICE_RELAY_OFFSET_CHANNEL_ID, &channelId, sizeof(uint16_t)); + const unsigned short sequence = 1; + std::memcpy(f.data() + RAKVOICE_RELAY_OFFSET_SEQUENCE, &sequence, sizeof(unsigned short)); + return f; + } + + struct RelayRig + { + SimulatedNetwork net; + RakVoice voice; + RakPeer *server = nullptr; + RakPeer *talker = nullptr; + RakPeer *listener = nullptr; + + bool Start() + { + server = net.AddPeer(4, 4); + voice.SetRelayHost(true); + server->AttachPlugin(&voice); + talker = net.AddPeer(1); + listener = net.AddPeer(1); + return net.Connect(talker, server) && net.Connect(listener, server); + } + + void Talk() + { + const std::vector frame = RelayFrameFrom(talker->GetMyGUID()); + talker->Send((const char *) frame.data(), (int) frame.size(), Priority::High, Reliability::Unreliable, 0, + net.AddressOf(server), false); + } + + ~RelayRig() { server->DetachPlugin(&voice); } + }; +} + +TEST(RakVoiceNetworkThreadRelay, HandlerRelaysFramesWhileServerReceiveIsStalled) +{ + RelayRig rig; + ASSERT_TRUE(rig.Start()); + const RakNetGUID listener = rig.listener->GetMyGUID(); + rig.voice.SetRelayFrameHandler([&](Packet *packet) { rig.voice.RelayFrame(packet, &listener, 1); }); + + rig.net.SetReceiveEnabled(rig.server, false); + rig.Talk(); + ASSERT_TRUE(rig.net.RunUntilReceived(rig.listener, ID_RAKVOICE_RELAY_DATA, 2000)); + + const SimulatedNetwork::Message *relayed = rig.net.Find(rig.listener, ID_RAKVOICE_RELAY_DATA); + Packet view {}; + view.data = const_cast(relayed->data.data()); + view.length = (unsigned int) relayed->data.size(); + EXPECT_EQ(rig.talker->GetMyGUID(), RakVoice::ReadRelayOrigin(&view)); + EXPECT_EQ(rig.server->GetMyGUID(), relayed->guid); +} + +TEST(RakVoiceNetworkThreadRelay, FramesTakenByTheHandlerNeverReachTheApplication) +{ + RelayRig rig; + ASSERT_TRUE(rig.Start()); + std::atomic handled {0}; + rig.voice.SetRelayFrameHandler([&](Packet *) { ++handled; }); + + rig.Talk(); + ASSERT_TRUE(rig.net.RunUntil([&] { return handled.load() == 1; }, 2000)); + rig.net.Step(50); + EXPECT_FALSE(rig.net.Received(rig.server, ID_RAKVOICE_RELAY_DATA)); +} + +TEST(RakVoiceNetworkThreadRelay, WithoutAHandlerTheRelayHostStillPassesFramesToReceive) +{ + RelayRig rig; + ASSERT_TRUE(rig.Start()); + + rig.Talk(); + EXPECT_TRUE(rig.net.RunUntilReceived(rig.server, ID_RAKVOICE_RELAY_DATA, 2000)); +} + +TEST(RakVoiceNetworkThreadRelay, ClearingTheHandlerRestoresPassThrough) +{ + RelayRig rig; + ASSERT_TRUE(rig.Start()); + std::atomic handled {0}; + rig.voice.SetRelayFrameHandler([&](Packet *) { ++handled; }); + rig.voice.SetRelayFrameHandler(nullptr); + + rig.Talk(); + ASSERT_TRUE(rig.net.RunUntilReceived(rig.server, ID_RAKVOICE_RELAY_DATA, 2000)); + EXPECT_EQ(0, handled.load()); +} + +TEST(RakVoiceNetworkThreadRelay, HandlerIsIgnoredWhenNotARelayHost) +{ + // A client never relays; a handler left set must not swallow its frames. + RelayRig rig; + ASSERT_TRUE(rig.Start()); + rig.voice.SetRelayHost(false); + std::atomic handled {0}; + rig.voice.SetRelayFrameHandler([&](Packet *) { ++handled; }); + + rig.Talk(); + rig.net.Step(200); + EXPECT_EQ(0, handled.load()); +} From 792770208464f1c1b4dbcba967689d7db1a7f916 Mon Sep 17 00:00:00 2001 From: Segfault <5221072+Segfaultd@users.noreply.github.com> Date: Sun, 18 Oct 2026 11:03:32 +0200 Subject: [PATCH 2/8] feat(voice): relay on the network thread unconditionally OnReceiveNetworkThread is now offered to every attached plugin, with no UsesNetworkThreadReceive() opt-in; the default lets the message continue to Receive(). SetRelayHost() takes the relay function itself, so a relay host always relays on the network thread and relay frames never reach Receive(). SetRelayFrameHandler() and the pass-through to the application loop are gone; SetRelayHost(nullptr) stops relaying. --- Source/include/mafianet/peer.h | 4 +- Source/include/mafianet/plugin_interface2.h | 5 -- Source/include/mafianet/voice.h | 27 ++++----- Source/src/peer.cpp | 11 ++-- Source/src/voice.cpp | 21 +++---- .../RakVoiceNetworkThreadRelayLiveTests.cpp | 9 ++- Tests/Unit/NetworkThreadReceiveTests.cpp | 34 ++++++++---- .../Unit/RakVoiceNetworkThreadRelayTests.cpp | 55 ++++++------------- 8 files changed, 68 insertions(+), 98 deletions(-) diff --git a/Source/include/mafianet/peer.h b/Source/include/mafianet/peer.h index a3621952e..a375ab19e 100644 --- a/Source/include/mafianet/peer.h +++ b/Source/include/mafianet/peer.h @@ -994,8 +994,8 @@ class RAK_DLL_EXPORT RakPeer : public RakPeerInterface, public RNS2EventHandler DataStructures::List banList; // Threadsafe, and not thread safe DataStructures::List pluginListTS, pluginListNTS; - // Plugins that opted into OnReceiveNetworkThread. Also in one of the lists above. The mutex - // lets AttachPlugin/DetachPlugin run while the update thread is dispatching. + // Every attached plugin again, for OnReceiveNetworkThread. The mutex lets AttachPlugin and + // DetachPlugin run while the update thread is dispatching. DataStructures::List pluginListNetworkThread; SimpleMutex pluginListNetworkThreadMutex; diff --git a/Source/include/mafianet/plugin_interface2.h b/Source/include/mafianet/plugin_interface2.h index 5c293dfb8..88fca4e35 100644 --- a/Source/include/mafianet/plugin_interface2.h +++ b/Source/include/mafianet/plugin_interface2.h @@ -139,17 +139,12 @@ class RAK_DLL_EXPORT PluginInterface2 /// If true, then you cannot call RakPeer::AttachPlugin() or RakPeer::DetachPlugin() for this plugin, while RakPeer is active virtual bool UsesReliabilityLayer(void) const {return false;} - /// Queried when attached to RakPeer - /// Return true to have OnReceiveNetworkThread() called. Queried once, at AttachPlugin(). - virtual bool UsesNetworkThreadReceive(void) const {return false;} - /// Called on RakPeer's network update thread for each message from a connected system, before /// it is queued for RakPeer::Receive(). Use it for traffic that must not wait for the /// application to call Receive(), such as voice. Connection notifications are not routed here. /// Runs concurrently with the user thread, so anything the plugin shares with its other /// callbacks must be synchronized. RakPeer::Send() is safe to call from here; AttachPlugin() and /// DetachPlugin() are not (they wait for this call to return). ID_TIMESTAMP payloads arrive unshifted. - /// \pre To be called, UsesNetworkThreadReceive() must return true /// \param[in] packet The message. RR_STOP_PROCESSING_AND_DEALLOCATE frees it, RR_STOP_PROCESSING /// hands it to the plugin (free it with RakPeer::DeallocatePacket), and /// RR_CONTINUE_PROCESSING queues it for Receive() as usual. diff --git a/Source/include/mafianet/voice.h b/Source/include/mafianet/voice.h index e4e887708..ac3bdbf0d 100644 --- a/Source/include/mafianet/voice.h +++ b/Source/include/mafianet/voice.h @@ -310,22 +310,16 @@ class RAK_DLL_EXPORT RakVoice : public PluginInterface2 /// \return false, leaving both unchanged, if either value is outside 0..NUMBER_OF_ORDERED_STREAMS-1. bool SetOrderingChannels(char frameChannel, char controlChannel); - /// \brief Marks this peer as the relay host (the server). - /// A relay host never decodes: incoming ID_RAKVOICE_RELAY_DATA packets are passed - /// through to the application loop so they can be handed back to RelayFrame(). - /// Distinct from SetRelayMode(), which is what a talking/listening client enables. - void SetRelayHost(bool enable); - - /// rief Relays frames on RakPeer's network thread instead of the application loop. - /// With a handler set, a relay host hands each incoming ID_RAKVOICE_RELAY_DATA packet to - ///  handler on the network update thread and frees it afterwards, so the frame never - /// reaches Receive() and is forwarded even while the application's main thread is busy. - /// The handler typically picks recipients and calls RelayFrame(), which is safe there; it must + /// \brief Makes this peer the relay host (the server), relaying with \a relay. + /// A relay host never decodes. Each incoming ID_RAKVOICE_RELAY_DATA packet is handed to + /// \a relay on RakPeer's network update thread and freed afterwards, so frames are forwarded + /// even while the application's main thread is busy, and never reach Receive(). + /// \a relay typically picks recipients and calls RelayFrame(), which is safe there; it must /// not keep the packet, and whatever it reads must be safe to read from that thread. - /// Pass nullptr to go back to passing frames through to Receive(). Once this returns, the - /// previous handler is not running and will not be called again, so do not call this from - /// inside the handler. Ignored unless relay host. - void SetRelayFrameHandler(std::function handler); + /// Pass nullptr to stop being a relay host. Once this returns, the previous \a relay is not + /// running and will not be called again, so do not call this from inside it. + /// Distinct from SetRelayMode(), which is what a talking/listening client enables. + void SetRelayHost(std::function relay); /// \brief Forwards a received relay frame to a set of recipients without decoding it. /// Costs one Send per recipient; no codec state is touched. @@ -367,7 +361,6 @@ class RAK_DLL_EXPORT RakVoice : public PluginInterface2 virtual void OnShutdown(void); virtual void Update(void); virtual PluginReceiveResult OnReceive(Packet *packet); - virtual bool UsesNetworkThreadReceive(void) const { return true; } virtual PluginReceiveResult OnReceiveNetworkThread(Packet *packet); virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); @@ -422,7 +415,7 @@ class RAK_DLL_EXPORT RakVoice : public PluginInterface2 int defaultSignalType; bool loopbackMode; bool relayMode; - // Read on the network thread by OnReceiveNetworkThread. + // Set together with relayFrameHandler; read on the network thread by OnReceiveNetworkThread. std::atomic relayHost; std::function relayFrameHandler; MafiaNet::SimpleMutex relayFrameHandlerMutex; diff --git a/Source/src/peer.cpp b/Source/src/peer.cpp index 9700ca210..e175056e7 100644 --- a/Source/src/peer.cpp +++ b/Source/src/peer.cpp @@ -2930,13 +2930,10 @@ void RakPeer::AttachPlugin( PluginInterface2 *plugin ) } } - if (plugin->UsesNetworkThreadReceive()) - { - pluginListNetworkThreadMutex.Lock(); - if (pluginListNetworkThread.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) - pluginListNetworkThread.Insert(plugin, _FILE_AND_LINE_); - pluginListNetworkThreadMutex.Unlock(); - } + pluginListNetworkThreadMutex.Lock(); + if (pluginListNetworkThread.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) + pluginListNetworkThread.Insert(plugin, _FILE_AND_LINE_); + pluginListNetworkThreadMutex.Unlock(); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Source/src/voice.cpp b/Source/src/voice.cpp index 6c50e098f..18da3ce17 100644 --- a/Source/src/voice.cpp +++ b/Source/src/voice.cpp @@ -158,17 +158,13 @@ void RakVoice::SetRelayTarget(RakNetGUID server) relayTarget = server; } -void RakVoice::SetRelayHost(bool enable) +void RakVoice::SetRelayHost(std::function relay) { - relayHost = enable; -} - -void RakVoice::SetRelayFrameHandler(std::function handler) -{ - // Swapped under the lock OnReceiveNetworkThread calls it under, so a handler being cleared is + // Swapped under the lock OnReceiveNetworkThread calls it under, so a relay being replaced is // never mid-call when this returns. relayFrameHandlerMutex.Lock(); - relayFrameHandler = std::move(handler); + relayHost = static_cast(relay); + relayFrameHandler = std::move(relay); relayFrameHandlerMutex.Unlock(); } @@ -750,12 +746,10 @@ PluginReceiveResult RakVoice::OnReceive(Packet *packet) OnVoiceData(packet); return RR_STOP_PROCESSING_AND_DEALLOCATE; case ID_RAKVOICE_RELAY_DATA: - // A relay host never decodes. Plugin OnReceive runs inside RakPeer::Receive, before - // the packet reaches the application loop, so swallowing it here would leave the - // server with nothing to hand to RelayFrame(). Pass it through instead. With a relay - // frame handler set, OnReceiveNetworkThread has already taken it and it never gets here. + // A relay host never decodes. OnReceiveNetworkThread relays these before they are + // queued; one only gets here if it was queued just before SetRelayHost() took effect. if (relayHost) - return RR_CONTINUE_PROCESSING; + return RR_STOP_PROCESSING_AND_DEALLOCATE; OnRelayVoiceData(packet); return RR_STOP_PROCESSING_AND_DEALLOCATE; } @@ -769,6 +763,7 @@ PluginReceiveResult RakVoice::OnReceiveNetworkThread(Packet *packet) return RR_CONTINUE_PROCESSING; relayFrameHandlerMutex.Lock(); + // Re-checked under the lock: SetRelayHost(nullptr) may have run since the test above. if (!relayFrameHandler) { relayFrameHandlerMutex.Unlock(); diff --git a/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp b/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp index 398ac41b3..56360a981 100644 --- a/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp +++ b/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp @@ -5,7 +5,7 @@ * license.txt file in the root directory of this source tree. */ -// A relay host with RakVoice::SetRelayFrameHandler forwards voice on RakPeer's own +// A RakVoice relay host forwards voice on RakPeer's own // update thread. The server here never calls Receive() at all -- the shape of an // application whose main thread is stalled -- and the listener must still hear the // talker. The unit suite proves the routing on the simulated network; this proves it @@ -62,8 +62,7 @@ TEST_F(RakVoiceNetworkThreadRelayLive, RelaysWhileTheServerNeverCallsReceive) std::atomic listenerGuid {UNASSIGNED_RAKNET_GUID.g}; std::atomic handled {0}; - voice.SetRelayHost(true); - voice.SetRelayFrameHandler([&](Packet *packet) { + voice.SetRelayHost([&](Packet *packet) { RakNetGUID to; to.g = listenerGuid.load(); voice.RelayFrame(packet, &to, 1); @@ -119,7 +118,7 @@ TEST_F(RakVoiceNetworkThreadRelayLive, RelaysWhileTheServerNeverCallsReceive) EXPECT_TRUE(heard) << "listener never heard a frame relayed on the server's network thread"; EXPECT_GT(handled.load(), 0); - // Stop the handler before the voice it captures can go away. - voice.SetRelayFrameHandler(nullptr); + // Stop relaying before the locals the relay captures can go away. + voice.SetRelayHost(nullptr); server->DetachPlugin(&voice); } diff --git a/Tests/Unit/NetworkThreadReceiveTests.cpp b/Tests/Unit/NetworkThreadReceiveTests.cpp index 7fa7d8fec..9733b05fa 100644 --- a/Tests/Unit/NetworkThreadReceiveTests.cpp +++ b/Tests/Unit/NetworkThreadReceiveTests.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT-style license found in the * license.txt file in the root directory of this source tree. * - * PluginInterface2::OnReceiveNetworkThread: a plugin that opts in sees every + * PluginInterface2::OnReceiveNetworkThread: every attached plugin sees each * message from a connected system inside the update cycle, before it is queued * for RakPeer::Receive(). These run on the SimulatedNetwork with the server's * Receive() switched off, which is what a stalled application main thread looks @@ -35,10 +35,6 @@ namespace class NetworkThreadProbe : public PluginInterface2 { public: - explicit NetworkThreadProbe(bool optIn = true) : optIn(optIn) {} - - bool UsesNetworkThreadReceive(void) const override { return optIn; } - PluginReceiveResult OnReceiveNetworkThread(Packet *packet) override { networkThreadIds.push_back(packet->data[0]); @@ -78,11 +74,23 @@ namespace return n; } - bool optIn; std::vector networkThreadIds; std::vector userThreadIds; }; + // Overrides only OnReceive, like every plugin written before the hook existed. + class ReceiveOnlyPlugin : public PluginInterface2 + { + public: + PluginReceiveResult OnReceive(Packet *packet) override + { + ids.push_back(packet->data[0]); + return RR_CONTINUE_PROCESSING; + } + + std::vector ids; + }; + void SendId(RakPeer *from, const SystemAddress &to, MessageID id) { BitStream bs; @@ -180,18 +188,20 @@ TEST(NetworkThreadReceive, PluginCanReplyFromTheHookWhileReceiveIsStalled) p.server->DetachPlugin(&probe); } -TEST(NetworkThreadReceive, PluginThatDoesNotOptInIsNeverCalledOnTheNetworkThread) +TEST(NetworkThreadReceive, PluginWithoutTheHookStillGetsMessagesOnReceive) { Pair p; - NetworkThreadProbe probe(false); - p.server->AttachPlugin(&probe); + ReceiveOnlyPlugin plugin; + p.server->AttachPlugin(&plugin); ASSERT_TRUE(p.Start()); SendId(p.client, p.net.AddressOf(p.server), ID_NT_CONSUMED); ASSERT_TRUE(p.net.RunUntilReceived(p.server, ID_NT_CONSUMED, 2000)); - EXPECT_EQ(0u, probe.NetworkThreadCount(ID_NT_CONSUMED)); - EXPECT_EQ(1u, probe.UserThreadCount(ID_NT_CONSUMED)); - p.server->DetachPlugin(&probe); + size_t seen = 0; + for (MessageID id : plugin.ids) + seen += id == ID_NT_CONSUMED ? 1 : 0; + EXPECT_EQ(1u, seen); + p.server->DetachPlugin(&plugin); } TEST(NetworkThreadReceive, DetachedPluginIsNoLongerCalled) diff --git a/Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp b/Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp index d763abc74..5ddff8bec 100644 --- a/Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp +++ b/Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp @@ -4,8 +4,8 @@ * This source code is licensed under the MIT-style license found in the * license.txt file in the root directory of this source tree. * - * RakVoice::SetRelayFrameHandler: a relay host with a handler takes - * ID_RAKVOICE_RELAY_DATA on the network thread and forwards it there, so voice + * RakVoice::SetRelayHost: a relay host takes ID_RAKVOICE_RELAY_DATA on the + * network thread and forwards it there, so voice * keeps flowing while the server application's main thread is stalled. The * server's Receive() is switched off on the SimulatedNetwork to show it. */ @@ -49,7 +49,6 @@ namespace bool Start() { server = net.AddPeer(4, 4); - voice.SetRelayHost(true); server->AttachPlugin(&voice); talker = net.AddPeer(1); listener = net.AddPeer(1); @@ -67,12 +66,12 @@ namespace }; } -TEST(RakVoiceNetworkThreadRelay, HandlerRelaysFramesWhileServerReceiveIsStalled) +TEST(RakVoiceNetworkThreadRelay, RelaysFramesWhileServerReceiveIsStalled) { RelayRig rig; ASSERT_TRUE(rig.Start()); const RakNetGUID listener = rig.listener->GetMyGUID(); - rig.voice.SetRelayFrameHandler([&](Packet *packet) { rig.voice.RelayFrame(packet, &listener, 1); }); + rig.voice.SetRelayHost([&](Packet *packet) { rig.voice.RelayFrame(packet, &listener, 1); }); rig.net.SetReceiveEnabled(rig.server, false); rig.Talk(); @@ -86,51 +85,33 @@ TEST(RakVoiceNetworkThreadRelay, HandlerRelaysFramesWhileServerReceiveIsStalled) EXPECT_EQ(rig.server->GetMyGUID(), relayed->guid); } -TEST(RakVoiceNetworkThreadRelay, FramesTakenByTheHandlerNeverReachTheApplication) +TEST(RakVoiceNetworkThreadRelay, RelayedFramesNeverReachTheApplication) { RelayRig rig; ASSERT_TRUE(rig.Start()); - std::atomic handled {0}; - rig.voice.SetRelayFrameHandler([&](Packet *) { ++handled; }); + std::atomic relayed {0}; + rig.voice.SetRelayHost([&](Packet *) { ++relayed; }); rig.Talk(); - ASSERT_TRUE(rig.net.RunUntil([&] { return handled.load() == 1; }, 2000)); + ASSERT_TRUE(rig.net.RunUntil([&] { return relayed.load() == 1; }, 2000)); rig.net.Step(50); EXPECT_FALSE(rig.net.Received(rig.server, ID_RAKVOICE_RELAY_DATA)); } -TEST(RakVoiceNetworkThreadRelay, WithoutAHandlerTheRelayHostStillPassesFramesToReceive) +TEST(RakVoiceNetworkThreadRelay, ClearingTheRelayHostStopsRelaying) { RelayRig rig; ASSERT_TRUE(rig.Start()); - - rig.Talk(); - EXPECT_TRUE(rig.net.RunUntilReceived(rig.server, ID_RAKVOICE_RELAY_DATA, 2000)); -} - -TEST(RakVoiceNetworkThreadRelay, ClearingTheHandlerRestoresPassThrough) -{ - RelayRig rig; - ASSERT_TRUE(rig.Start()); - std::atomic handled {0}; - rig.voice.SetRelayFrameHandler([&](Packet *) { ++handled; }); - rig.voice.SetRelayFrameHandler(nullptr); - - rig.Talk(); - ASSERT_TRUE(rig.net.RunUntilReceived(rig.server, ID_RAKVOICE_RELAY_DATA, 2000)); - EXPECT_EQ(0, handled.load()); -} - -TEST(RakVoiceNetworkThreadRelay, HandlerIsIgnoredWhenNotARelayHost) -{ - // A client never relays; a handler left set must not swallow its frames. - RelayRig rig; - ASSERT_TRUE(rig.Start()); - rig.voice.SetRelayHost(false); - std::atomic handled {0}; - rig.voice.SetRelayFrameHandler([&](Packet *) { ++handled; }); + const RakNetGUID listener = rig.listener->GetMyGUID(); + std::atomic relayed {0}; + rig.voice.SetRelayHost([&](Packet *packet) { + ++relayed; + rig.voice.RelayFrame(packet, &listener, 1); + }); + rig.voice.SetRelayHost(nullptr); rig.Talk(); rig.net.Step(200); - EXPECT_EQ(0, handled.load()); + EXPECT_EQ(0, relayed.load()); + EXPECT_FALSE(rig.net.Received(rig.listener, ID_RAKVOICE_RELAY_DATA)); } From 205afd43b80b3997baac7f2a99a29f6b62e0f3bd Mon Sep 17 00:00:00 2001 From: Segfault <5221072+Segfaultd@users.noreply.github.com> Date: Sun, 18 Oct 2026 11:48:07 +0200 Subject: [PATCH 3/8] fix(voice): close the network-thread hook's shutdown and re-entry gaps Shutdown() calls OnRakPeerShutdown while the update thread is still running, so a plugin tearing down could race its own hook. Shutdown now stops the hook first, waiting out a call in flight; Startup() resumes it. AttachPlugin/DetachPlugin from inside the hook deadlocked on POSIX and would change the plugin lists Receive() walks on the user thread; they are now ignored there. SetRelayHost from inside the relay replaced the std::function while it ran; the change is now applied once it returns. Covers dispatch order, first-consumer-wins, double attach, late attach, unshifted ID_TIMESTAMP, spoofed origins, stragglers queued before becoming relay host, frame bursts, and the threaded guarantees: detach and SetRelayHost(nullptr) wait for a call in flight, no hook after shutdown starts, hook resumes after a restart. --- Source/include/mafianet/peer.h | 9 + Source/include/mafianet/plugin_interface2.h | 4 +- Source/include/mafianet/voice.h | 9 +- Source/src/peer.cpp | 14448 ++++++++-------- Source/src/voice.cpp | 18 + .../NetworkThreadReceiveLiveTests.cpp | 194 + .../RakVoiceNetworkThreadRelayLiveTests.cpp | 66 + Tests/Unit/NetworkThreadReceiveTests.cpp | 162 + .../Unit/RakVoiceNetworkThreadRelayTests.cpp | 206 + 9 files changed, 7901 insertions(+), 7215 deletions(-) create mode 100644 Tests/Integration/NetworkThreadReceiveLiveTests.cpp diff --git a/Source/include/mafianet/peer.h b/Source/include/mafianet/peer.h index a375ab19e..d42bf6409 100644 --- a/Source/include/mafianet/peer.h +++ b/Source/include/mafianet/peer.h @@ -42,6 +42,7 @@ #include "mafianet/lockless_types.h" #include "mafianet/ds_queue.h" #include +#include namespace MafiaNet { /// Forward declarations @@ -998,6 +999,14 @@ class RAK_DLL_EXPORT RakPeer : public RakPeerInterface, public RNS2EventHandler // DetachPlugin run while the update thread is dispatching. DataStructures::List pluginListNetworkThread; SimpleMutex pluginListNetworkThreadMutex; + // The thread inside DispatchNetworkThreadReceive. AttachPlugin/DetachPlugin called from a hook + // on it are ignored: they would deadlock on the lock that thread holds, and they also change + // the plugin lists Receive() walks on the user thread. + std::atomic networkThreadDispatcher; + // Set once Shutdown() starts telling plugins, cleared by Startup(). Guarded by the mutex above. + bool networkThreadReceiveStopped; + bool IsInsideNetworkThreadReceive(void) const; + void SetNetworkThreadReceiveStopped(bool stopped); DataStructures::Queue requestedConnectionQueue; SimpleMutex requestedConnectionQueueMutex; diff --git a/Source/include/mafianet/plugin_interface2.h b/Source/include/mafianet/plugin_interface2.h index 88fca4e35..d85332a6a 100644 --- a/Source/include/mafianet/plugin_interface2.h +++ b/Source/include/mafianet/plugin_interface2.h @@ -144,7 +144,9 @@ class RAK_DLL_EXPORT PluginInterface2 /// application to call Receive(), such as voice. Connection notifications are not routed here. /// Runs concurrently with the user thread, so anything the plugin shares with its other /// callbacks must be synchronized. RakPeer::Send() is safe to call from here; AttachPlugin() and - /// DetachPlugin() are not (they wait for this call to return). ID_TIMESTAMP payloads arrive unshifted. + /// DetachPlugin() do nothing when called from here, and Shutdown() must not be called from here. + /// Not called once Shutdown() has begun calling OnRakPeerShutdown(). ID_TIMESTAMP payloads arrive + /// unshifted. /// \param[in] packet The message. RR_STOP_PROCESSING_AND_DEALLOCATE frees it, RR_STOP_PROCESSING /// hands it to the plugin (free it with RakPeer::DeallocatePacket), and /// RR_CONTINUE_PROCESSING queues it for Receive() as usual. diff --git a/Source/include/mafianet/voice.h b/Source/include/mafianet/voice.h index ac3bdbf0d..941768d38 100644 --- a/Source/include/mafianet/voice.h +++ b/Source/include/mafianet/voice.h @@ -28,6 +28,7 @@ #include #include +#include // Forward declarations for Opus and RNNoise struct OpusEncoder; @@ -317,7 +318,8 @@ class RAK_DLL_EXPORT RakVoice : public PluginInterface2 /// \a relay typically picks recipients and calls RelayFrame(), which is safe there; it must /// not keep the packet, and whatever it reads must be safe to read from that thread. /// Pass nullptr to stop being a relay host. Once this returns, the previous \a relay is not - /// running and will not be called again, so do not call this from inside it. + /// running and will not be called again. Called from inside \a relay, the change takes + /// effect as soon as that call returns. /// Distinct from SetRelayMode(), which is what a talking/listening client enables. void SetRelayHost(std::function relay); @@ -419,6 +421,11 @@ class RAK_DLL_EXPORT RakVoice : public PluginInterface2 std::atomic relayHost; std::function relayFrameHandler; MafiaNet::SimpleMutex relayFrameHandlerMutex; + // The thread running relayFrameHandler, and a SetRelayHost() it made from inside: applied once + // the handler returns, since replacing a std::function while it runs destroys it mid-call. + std::atomic relayThread; + std::function pendingRelay; + bool hasPendingRelay; bool perSpeakerOutput; /// Concurrent relay decoders the application allows; 0 means only the memory backstop. unsigned maxDecodedSpeakers; diff --git a/Source/src/peer.cpp b/Source/src/peer.cpp index e175056e7..41ffe1b80 100644 --- a/Source/src/peer.cpp +++ b/Source/src/peer.cpp @@ -1,7213 +1,7235 @@ -/* - * Original work: Copyright (c) 2014, Oculus VR, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * RakNet License.txt file in the licenses directory of this source tree. An additional grant - * of patent rights can be found in the RakNet Patents.txt file in the same directory. - * - * - * Modified work: Copyright (c) 2016-2020, SLikeSoft UG (haftungsbeschränkt) - * - * This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style - * license found in the license.txt file in the root directory of this source tree. - */ - -// \file -// - - - -#define CAT_NEUTER_EXPORT /* Neuter dllimport for libcat */ - -#include "mafianet/defines.h" -#include "mafianet/peer.h" -#include "mafianet/session_admission.h" -#include "mafianet/types.h" - -#ifdef _WIN32 - -#else -#include -#endif - -// #if defined(new) -// #pragma push_macro("new") -// #undef new -// #define RMO_NEW_UNDEF_ALLOCATING_QUEUE -// #endif - -#include -#include // toupper -#include -#include "mafianet/get_time.h" -#include -#include -#include "mafianet/message_identifiers.h" -#include "mafianet/mtu_black_hole.h" -#include "mafianet/ds_huffman_encoding_tree.h" -#include "mafianet/rand.h" -#include "mafianet/plugin_interface2.h" -#include "mafianet/string_compressor.h" -#include "mafianet/string_table.h" -#include "mafianet/network_id_object.h" -#include "mafianet/types.h" -#include "mafianet/dr_sha1.h" -#include "mafianet/sleep.h" -#include "mafianet/assert.h" -#include "mafianet/version.h" -#include "mafianet/network_id_manager.h" -#include "mafianet/get_time_of_day.h" -#include "mafianet/signaled_event.h" -#include "mafianet/super_fast_hash.h" -#include "mafianet/alloca.h" -#include "mafianet/wsa_startup_singleton.h" -#include "mafianet/linux_adapter.h" -#include "mafianet/osx_adapter.h" - -#ifdef USE_THREADED_SEND -#include "mafianet/send_to_thread.h" -#endif - -#ifdef CAT_AUDIT -#define CAT_AUDIT_PRINTF(...) printf(__VA_ARGS__) -#else -#define CAT_AUDIT_PRINTF(...) -#endif - -namespace MafiaNet -{ -RAK_THREAD_DECLARATION(UpdateNetworkLoop); -RAK_THREAD_DECLARATION(RecvFromLoop); -RAK_THREAD_DECLARATION(UDTConnect); -} -#define REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE 8 - -#if !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ ) -#include // malloc -#endif - - - -#if defined(_WIN32) -// -#else -/* -#include // Console 2 -#include -extern bool _extern_Console2LoadModules(void); -extern int _extern_Console2GetConnectionStatus(void); -extern int _extern_Console2GetLobbyStatus(void); -//extern bool Console2StartupFluff(unsigned int *); -extern void Console2ShutdownFluff(void); -//extern unsigned int Console2ActivateConnection(unsigned int, void *); -//extern bool Console2BlockOnEstablished(void); -extern void Console2GetIPAndPort(unsigned int, char *, unsigned short *, unsigned int ); -//extern void Console2DeactivateConnection(unsigned int, unsigned int); -*/ -#endif - - -// Probed high to low while connecting: the connecting peer pads -// ID_OPEN_CONNECTION_REQUEST_1 to a rung and steps down when nothing comes back, -// so the negotiated MTU is the largest rung that survived the path. Each rung is -// a whole IP datagram, MAXIMUM_MTU_SIZE first. -// -// 1280 is both the IPv6 minimum MTU and what WireGuard-derived tunnels commonly -// settle on, which makes it the single most valuable intermediate rung; 1024 -// covers heavier or stacked encapsulation; 576 is the dial-up floor. The gap -// from MAXIMUM_MTU_SIZE straight to 1200 that used to sit here meant a peer one -// byte over the top rung gave up ~20% of its payload capacity to find that out. -// -// The ladder itself lives in mtu_black_hole.h: the in-session black-hole -// step-down (ReliabilityLayer) walks the same rungs this handshake probes. -static const int NUM_MTU_SIZES=MafiaNet::MTU_LADDER_SIZE; -static const int *const mtuSizes=MafiaNet::MTU_LADDER; - -// How many connection attempts are spent on each rung of mtuSizes before -// stepping down. -// -// Never zero. Connect() takes sendConnectionAttemptCount from the caller and -// documents no lower bound, so a caller asking for fewer attempts than there are -// rungs used to divide by zero here -- a crash that was unreachable only because -// the default (12) happened to exceed NUM_MTU_SIZES. Such a caller now gets one -// attempt per rung and stops when its budget runs out, having tried the largest -// sizes first. -static unsigned GetConnectionAttemptsPerMTU(unsigned sendConnectionAttemptCount) -{ - const unsigned attemptsPerMTU = sendConnectionAttemptCount / (unsigned) NUM_MTU_SIZES; - return attemptsPerMTU > 0 ? attemptsPerMTU : 1; -} - - -// Note to self - if I change this it might affect RECIPIENT_OFFLINE_MESSAGE_INTERVAL in Natpunchthrough.cpp -//static const int MAX_OPEN_CONNECTION_REQUESTS=8; -//static const int TIME_BETWEEN_OPEN_CONNECTION_REQUESTS=500; - -using namespace MafiaNet; - -static RakNetRandom rnr; - -/* -struct RakPeerAndIndex -{ - RakNetSocket2 *s; - RakPeer *rakPeer; -}; -*/ - -static const unsigned int MAX_OFFLINE_DATA_LENGTH=400; // I set this because I limit ID_CONNECTION_REQUEST to 512 bytes, and the password is appended to that packet. - -// Used to distinguish between offline messages with data, and messages from the reliability layer -// Should be different than any message that could result from messages from the reliability layer -// Make sure highest bit is 0, so isValid in DatagramHeaderFormat is false -static const unsigned char OFFLINE_MESSAGE_DATA_ID[16]={0x00,0xFF,0xFF,0x00,0xFE,0xFE,0xFE,0xFE,0xFD,0xFD,0xFD,0xFD,0x12,0x34,0x56,0x78}; - -struct PacketFollowedByData -{ - Packet p; - unsigned char data[1]; -}; - -Packet *RakPeer::AllocPacket(unsigned dataSize, const char *file, unsigned int line) -{ - // Crashes when dataSize is 4 bytes - not sure why -// unsigned char *data = (unsigned char *) rakMalloc_Ex(sizeof(PacketFollowedByData)+dataSize, file, line); -// Packet *p = &((PacketFollowedByData *)data)->p; -// p->data=((PacketFollowedByData *)data)->data; -// p->length=dataSize; -// p->bitSize=BYTES_TO_BITS(dataSize); -// p->deleteData=false; -// p->guid=UNASSIGNED_RAKNET_GUID; -// return p; - - MafiaNet::Packet *p; - packetAllocationPoolMutex.Lock(); - p = packetAllocationPool.Allocate(file,line); - ++packetsOutstanding; - packetAllocationPoolMutex.Unlock(); - p = new ((void*)p) Packet; - p->data=(unsigned char*) rakMalloc_Ex(dataSize,file,line); - p->length=dataSize; - p->bitSize=BYTES_TO_BITS(dataSize); - p->deleteData=true; - p->guid=UNASSIGNED_RAKNET_GUID; - p->wasGeneratedLocally=false; - return p; -} - -Packet *RakPeer::AllocPacket(unsigned dataSize, unsigned char *data, const char *file, unsigned int line) -{ - // Packet *p = (Packet *)rakMalloc_Ex(sizeof(Packet), file, line); - MafiaNet::Packet *p; - packetAllocationPoolMutex.Lock(); - p = packetAllocationPool.Allocate(file,line); - ++packetsOutstanding; - packetAllocationPoolMutex.Unlock(); - p = new ((void*)p) Packet; - RakAssert(p); - p->data=data; - p->length=dataSize; - p->bitSize=BYTES_TO_BITS(dataSize); - p->deleteData=true; - p->guid=UNASSIGNED_RAKNET_GUID; - p->wasGeneratedLocally=false; - return p; -} - -STATIC_FACTORY_DEFINITIONS(RakPeerInterface,RakPeer) - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Constructor -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RakPeer() -{ -#if LIBCAT_SECURITY==1 - // Encryption and security - CAT_AUDIT_PRINTF("AUDIT: Initializing RakPeer security flags: using_security = false, server_handshake = null, cookie_jar = null\n"); - _using_security = false; - _server_handshake = 0; - _cookie_jar = 0; -#endif - - StringCompressor::AddReference(); - MafiaNet::StringTable::AddReference(); - WSAStartupSingleton::AddRef(); - - defaultMTUSize = mtuSizes[NUM_MTU_SIZES-1]; - trackFrequencyTable = false; - maximumIncomingConnections = 0; - maximumNumberOfPeers = 0; - //remoteSystemListSize=0; - remoteSystemList = 0; - activeSystemList = 0; - activeSystemListSize=0; - remoteSystemLookup=0; - bytesSentPerSecond = bytesReceivedPerSecond = 0; - endThreads = true; - isMainLoopThreadActive = false; - socketFactory = nullptr; - manualUpdate = false; - incomingDatagramEventHandler=0; - sessionConfigInteractive=false; - maximumPendingSessions=0; - maximumPendingSessionsPerAddress=0; - sessionTimeout=0; - - - - - - // isRecvfromThreadActive=false; -#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 - occasionalPing = true; -#else - occasionalPing = false; -#endif - allowInternalRouting=false; - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - ipList[i]=UNASSIGNED_SYSTEM_ADDRESS; - allowConnectionResponseIPMigration = false; - //incomingPasswordLength=outgoingPasswordLength=0; - incomingPasswordLength=0; - splitMessageProgressInterval=0; - //unreliableTimeout=0; - unreliableTimeout=1000; - maxOutgoingBPS=0; - firstExternalID=UNASSIGNED_SYSTEM_ADDRESS; - myGuid=UNASSIGNED_RAKNET_GUID; - userUpdateThreadPtr=0; - userUpdateThreadData=0; - -#ifdef _DEBUG - // Wait longer to disconnect in debug so I don't get disconnected while tracing - defaultTimeoutTime=30000; -#else - defaultTimeoutTime=10000; -#endif - -#ifdef _DEBUG - _packetloss=0.0; - _minExtraPing=0; - _extraPingVariance=0; -#endif - - bufferedCommands.SetPageSize(sizeof(BufferedCommandStruct)*16); - socketQueryOutput.SetPageSize(sizeof(SocketQueryOutput)*8); - - packetAllocationPoolMutex.Lock(); - packetAllocationPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); - packetsOutstanding = 0; - packetAllocationPoolMutex.Unlock(); - shutdownGeneration = 0; - - remoteSystemIndexPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - GenerateGUID(); - - quitAndDataEvents.InitEvent(); - limitConnectionFrequencyFromTheSameIP=false; - ResetSendReceipt(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Destructor -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::~RakPeer() -{ - Shutdown( 0, 0 ); - - // Free the ban list. - ClearBanList(); - - StringCompressor::RemoveReference(); - MafiaNet::StringTable::RemoveReference(); - WSAStartupSingleton::Deref(); - - quitAndDataEvents.CloseEvent(); - -#if LIBCAT_SECURITY==1 - // Encryption and security - CAT_AUDIT_PRINTF("AUDIT: Deleting RakPeer security objects, handshake = %x, cookie jar = %x\n", _server_handshake, _cookie_jar); - if (_server_handshake) MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - if (_cookie_jar) MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); -#endif - - - - - - - - - - - - - - - - -// for (unsigned int i=0; i < pluginListTS.Size(); i++) -// pluginListTS[i]->SetRakPeerInterface(0); -// for (unsigned int i=0; i < pluginListNTS.Size(); i++) -// pluginListNTS[i]->SetRakPeerInterface(0); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// \brief Starts the network threads, opens the listen port. -// You must call this before calling Connect(). -// Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). -// \note Call SetMaximumIncomingConnections if you want to accept incoming connections -// \param[in] maxConnections The maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.- A hybrid would set it to the sum of both types of connections -// \param[in] localPort The port to listen for connections on. -// \param[in] _threadSleepTimer How many ms to Sleep each internal update cycle. With new congestion control, the best results will be obtained by passing 10. -// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass &socketDescriptor, 1SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); -// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. -// \return False on failure (can't create socket or thread), true on success. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -StartupResult RakPeer::Startup( unsigned int maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority ) -{ - if (IsActive()) - return RAKNET_ALREADY_STARTED; - - // If getting the guid failed in the constructor, try again - if (myGuid.g==0) - { - GenerateGUID(); - if (myGuid.g==0) - return COULD_NOT_GENERATE_GUID; - } - - if (threadPriority==-99999) - { - - -#if defined(_WIN32) - threadPriority=0; - - - - -#else - threadPriority=1000; -#endif - } - - - FillIPList(); - - if (myGuid==UNASSIGNED_RAKNET_GUID) - { - rnr.SeedMT( GenerateSeedFromGuid() ); - } - - //RakPeerAndIndex rpai[32]; - //RakAssert(socketDescriptorCount<32); - - RakAssert(socketDescriptors && socketDescriptorCount>=1); - - if (socketDescriptors==0 || socketDescriptorCount<1) - return INVALID_SOCKET_DESCRIPTORS; - - //unsigned short localPort; - //localPort=socketDescriptors[0].port; - - RakAssert( maxConnections > 0 ); - - if ( maxConnections <= 0 ) - return INVALID_MAX_CONNECTIONS; - - DerefAllSockets(); - - - unsigned i; - // Go through all socket descriptors and precreate sockets on the specified addresses - for (i=0; iCreate(socketDescriptors[i], i) : RakNetSocket2Allocator::AllocRNS2(); - if (r2 == nullptr) - { - DerefAllSockets(); - return SOCKET_PORT_ALREADY_IN_USE; - } - r2->SetUserConnectionSocketIndex(i); - if (r2->IsBerkleySocket()) - { - RNS2_BerkleyBindParameters bbp; - bbp.port=socketDescriptors[i].port; - bbp.hostAddress=(char*) socketDescriptors[i].hostAddress; - bbp.addressFamily=socketDescriptors[i].socketFamily; - bbp.type=SOCK_DGRAM; - bbp.protocol=socketDescriptors[i].extraSocketOptions; - bbp.nonBlockingSocket=false; - bbp.setBroadcast=true; - bbp.setIPHdrIncl=false; - bbp.doNotFragment=false; - bbp.pollingThreadPriority=threadPriority; - bbp.eventHandler=this; - RNS2BindResult br = ((RNS2_Berkley*) r2)->Bind(&bbp, _FILE_AND_LINE_); - - if ( - #if RAKNET_SUPPORT_IPV6==0 - socketDescriptors[i].socketFamily!=AF_INET || - #endif - br==BR_REQUIRES_RAKNET_SUPPORT_IPV6_DEFINE) - { - RakNetSocket2Allocator::DeallocRNS2(r2); - DerefAllSockets(); - return SOCKET_FAMILY_NOT_SUPPORTED; - } - else if (br==BR_FAILED_TO_BIND_SOCKET) - { - RakNetSocket2Allocator::DeallocRNS2(r2); - DerefAllSockets(); - return SOCKET_PORT_ALREADY_IN_USE; - } - else if (br==BR_FAILED_SEND_TEST) - { - RakNetSocket2Allocator::DeallocRNS2(r2); - DerefAllSockets(); - return SOCKET_FAILED_TEST_SEND; - } - else - { - RakAssert(br==BR_SUCCESS); - } - } - else - { - // Factory-supplied: already bound; it delivers datagrams to us. - r2->SetRecvEventHandler(this); - } - - socketList.Push(r2, _FILE_AND_LINE_ ); - - } - - for (i=0; iIsBerkleySocket()) - ((RNS2_Berkley*) socketList[i])->CreateRecvPollingThread(threadPriority); - } - - for (i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - { - if (ipList[i]==UNASSIGNED_SYSTEM_ADDRESS) - break; - // #high - using the 1st socket here is flawed - in cases of having multiple sockets (f.e. different ports and different families (i.e. IPv4/IPv6) we must use the proper - // socket for each IP address in the list - if (socketList[0]->IsBerkleySocket()) - { - unsigned short port = ((RNS2_Berkley*)socketList[0])->GetBoundAddress().GetPort(); - ipList[i].SetPortHostOrder(port); - - } - else - { - ipList[i].SetPortHostOrder(socketList[0]->GetBoundAddress().GetPort()); - } - } - - if ( maximumNumberOfPeers == 0 ) - { - // Don't allow more incoming connections than we have peers. - if ( maximumIncomingConnections > maxConnections ) - maximumIncomingConnections = maxConnections; - - maximumNumberOfPeers = maxConnections; - // 04/19/2006 - Don't overallocate because I'm no longer allowing connected pings. - // The disconnects are not consistently processed and the process was sloppy and complicated. - // Allocate 10% extra to handle new connections from players trying to connect when the server is full - //remoteSystemListSize = maxConnections;// * 11 / 10 + 1; - - // remoteSystemList in Single thread - //remoteSystemList = MafiaNet::OP_NEW( _FILE_AND_LINE_ ); - remoteSystemList = MafiaNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); - - remoteSystemLookup = MafiaNet::OP_NEW_ARRAY((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE, _FILE_AND_LINE_ ); - - activeSystemList = MafiaNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); - - for ( i = 0; i < maximumNumberOfPeers; i++ ) - //for ( i = 0; i < remoteSystemListSize; i++ ) - { - // remoteSystemList in Single thread - remoteSystemList[ i ].isActive = false; - remoteSystemList[ i ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; - remoteSystemList[ i ].guid = UNASSIGNED_RAKNET_GUID; - remoteSystemList[ i ].myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; - remoteSystemList[ i ].connectMode=RemoteSystemStruct::NO_ACTION; - remoteSystemList[ i ].MTUSize = defaultMTUSize; - remoteSystemList[ i ].remoteSystemIndex = (SystemIndex) i; - // One-time zero-init: the array is OP_NEW_ARRAY allocated with no member init, so prime the - // reason-payload fields here before ClearDisconnectReason() can ever free them. - remoteSystemList[ i ].disconnectReasonData = 0; - remoteSystemList[ i ].disconnectReasonLength = 0; - remoteSystemList[ i ].remoteSessionConfigData = 0; - remoteSystemList[ i ].remoteSessionConfigLength = 0; - remoteSystemList[ i ].withheldConnectionPacketData = 0; - remoteSystemList[ i ].withheldConnectionPacketLength = 0; - remoteSystemList[ i ].sessionConfigAwaitingLocalDecision = false; - remoteSystemList[ i ].sessionConfigIsConnectingSide = false; - remoteSystemList[ i ].connectionReportedToApplication = false; - remoteSystemList[ i ].sessionConfigStartTime = 0; -#ifdef _DEBUG - remoteSystemList[ i ].reliabilityLayer.ApplyNetworkSimulator(_packetloss, _minExtraPing, _extraPingVariance); -#endif - - // All entries in activeSystemList have valid pointers all the time. - activeSystemList[ i ] = &remoteSystemList[ i ]; - } - - for (i=0; i < (unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE; i++) - { - remoteSystemLookup[i]=0; - } - } - - // For histogram statistics - // nextReadBytesTime=0; - // lastSentBytes=lastReceivedBytes=0; - - if ( endThreads ) - { - updateCycleIsRunning = false; - endThreads = false; - firstExternalID=UNASSIGNED_SYSTEM_ADDRESS; - - ClearBufferedCommands(); - ClearBufferedPackets(); - ClearSocketQueryOutput(); - - if ( isMainLoopThreadActive == false && !manualUpdate ) - { -#if RAKPEER_USER_THREADED!=1 - - int errorCode; - - - - - - - - errorCode = MafiaNet::RakThread::Create(UpdateNetworkLoop, this, threadPriority); - - - if ( errorCode != 0 ) - { - Shutdown( 0, 0 ); - return FAILED_TO_CREATE_NETWORK_THREAD; - } -// RakAssert(isRecvFromLoopThreadActive.GetValue()==0); -#endif // RAKPEER_USER_THREADED!=1 - - /* - for (i=0; i(_FILE_AND_LINE_); - rpai->s=socketList[i]; - rpai->rakPeer=this; - -#if RAKPEER_USER_THREADED!=1 - - errorCode = MafiaNet::RakThread::Create(RecvFromLoop, rpai, threadPriority); - - if ( errorCode != 0 ) - { - Shutdown( 0, 0 ); - return FAILED_TO_CREATE_NETWORK_THREAD; - } -#endif // RAKPEER_USER_THREADED!=1 - } - */ - - - /* -#if RAKPEER_USER_THREADED!=1 - - while ( isRecvFromLoopThreadActive.GetValue() < (uint32_t) socketDescriptorCount ) - RakSleep(10); - #endif // RAKPEER_USER_THREADED!=1 - */ - - } - -#if RAKPEER_USER_THREADED!=1 - // Wait for the threads to activate. When they are active they will set these variables to true - while ( !manualUpdate && isMainLoopThreadActive == false ) - RakSleep(10); -#endif // RAKPEER_USER_THREADED!=1 - } - - for (i=0; i < pluginListTS.Size(); i++) - { - pluginListTS[i]->OnRakPeerStartup(); - } - - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginListNTS[i]->OnRakPeerStartup(); - } - -#ifdef USE_THREADED_SEND - MafiaNet::SendToThread::AddRef(); -#endif - - return RAKNET_STARTED; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Must be called while offline -// -// If you accept connections, you must call this or else security will not be enabled for incoming connections. -// -// This feature requires more round trips, bandwidth, and CPU time for the connection handshake -// x64 builds require under 25% of the CPU time of other builds -// -// See the Encryption sample for example usage -// -// Parameters: -// publicKey = A pointer to the public key for accepting new connections -// privateKey = A pointer to the private key for accepting new connections -// If the private keys are 0, then a new key will be generated when this function is called -// bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::InitializeSecurity(const char *public_key, const char *private_key, bool bRequireClientKey) -{ -#if LIBCAT_SECURITY==1 - if ( endThreads == false ) - return false; - - // Copy client public key requirement flag - _require_client_public_key = bRequireClientKey; - - if (_server_handshake) - { - CAT_AUDIT_PRINTF("AUDIT: Deleting old server_handshake %x\n", _server_handshake); - MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - } - if (_cookie_jar) - { - CAT_AUDIT_PRINTF("AUDIT: Deleting old cookie jar %x\n", _cookie_jar); - MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); - } - - _server_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); - _cookie_jar = MafiaNet::OP_NEW(_FILE_AND_LINE_); - - CAT_AUDIT_PRINTF("AUDIT: Created new server_handshake %x\n", _server_handshake); - CAT_AUDIT_PRINTF("AUDIT: Created new cookie jar %x\n", _cookie_jar); - CAT_AUDIT_PRINTF("AUDIT: Running _server_handshake->Initialize()\n"); - - if (_server_handshake->Initialize(public_key, private_key)) - { - CAT_AUDIT_PRINTF("AUDIT: Successfully initialized, filling cookie jar with goodies, storing public key and setting using security flag to true\n"); - - _server_handshake->FillCookieJar(_cookie_jar); - - memcpy(my_public_key, public_key, sizeof(my_public_key)); - - _using_security = true; - return true; - } - - CAT_AUDIT_PRINTF("AUDIT: Failure to initialize so deleting server handshake and cookie jar; also setting using_security flag = false\n"); - - MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - _server_handshake=0; - MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); - _cookie_jar=0; - _using_security = false; - return false; -#else - (void) public_key; - (void) private_key; - (void) bRequireClientKey; - - return false; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description -// Must be called while offline -// Disables security for incoming connections. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DisableSecurity( void ) -{ -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: DisableSecurity() called, so deleting _server_handshake %x and cookie_jar %x\n", _server_handshake, _cookie_jar); - MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - _server_handshake=0; - MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); - _cookie_jar=0; - - _using_security = false; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AddToSecurityExceptionList(const char *ip) -{ - securityExceptionMutex.Lock(); - securityExceptionList.Insert(RakString(ip), _FILE_AND_LINE_); - securityExceptionMutex.Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RemoveFromSecurityExceptionList(const char *ip) -{ - if (securityExceptionList.Size()==0) - return; - - if (ip==0) - { - securityExceptionMutex.Lock(); - securityExceptionList.Clear(false, _FILE_AND_LINE_); - securityExceptionMutex.Unlock(); - } - else - { - unsigned i=0; - securityExceptionMutex.Lock(); - while (i < securityExceptionList.Size()) - { - if (securityExceptionList[i].IPAddressMatch(ip)) - { - securityExceptionList[i]=securityExceptionList[securityExceptionList.Size()-1]; - securityExceptionList.RemoveAtIndex(securityExceptionList.Size()-1); - } - else - i++; - } - securityExceptionMutex.Unlock(); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsInSecurityExceptionList(const char *ip) -{ - if (securityExceptionList.Size()==0) - return false; - - unsigned i=0; - securityExceptionMutex.Lock(); - for (; i < securityExceptionList.Size(); i++) - { - if (securityExceptionList[i].IPAddressMatch(ip)) - { - securityExceptionMutex.Unlock(); - return true; - } - } - securityExceptionMutex.Unlock(); - return false; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sets how many incoming connections are allowed. If this is less than the number of players currently connected, no -// more players will be allowed to connect. If this is greater than the maximum number of peers allowed, it will be reduced -// to the maximum number of peers allowed. Defaults to 0. -// -// Parameters: -// numberAllowed - Maximum number of incoming connections allowed. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetMaximumIncomingConnections( unsigned short numberAllowed ) -{ - maximumIncomingConnections = numberAllowed; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the maximum number of incoming connections, which is always <= maxConnections -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetMaximumIncomingConnections( void ) const -{ - return maximumIncomingConnections; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns how many open connections there are at this time -// \return the number of open connections -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned short RakPeer::NumberOfConnections(void) const -{ - DataStructures::List addresses; - DataStructures::List guids; - GetSystemList(addresses, guids); - return (unsigned short) addresses.Size(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sets the password incoming connections must match in the call to Connect (defaults to none) -// Pass 0 to passwordData to specify no password -// -// Parameters: -// passwordData: A data block that incoming connections must match. This can be just a password, or can be a stream of data. -// - Specify 0 for no password data -// passwordDataLength: The length in bytes of passwordData -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetIncomingPassword( const char* passwordData, int passwordDataLength ) -{ - //if (passwordDataLength > MAX_OFFLINE_DATA_LENGTH) - // passwordDataLength=MAX_OFFLINE_DATA_LENGTH; - - if (passwordDataLength > 255) - passwordDataLength=255; - - if (passwordData==0) - passwordDataLength=0; - - // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? - // It won't overflow at least because incomingPasswordLength is an unsigned char - if (passwordDataLength>0) - memcpy(incomingPassword, passwordData, passwordDataLength); - incomingPasswordLength=(unsigned char)passwordDataLength; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetIncomingPassword( char* passwordData, int *passwordDataLength ) -{ - if (passwordData==0) - { - *passwordDataLength=incomingPasswordLength; - return; - } - - if (*passwordDataLength > incomingPasswordLength) - *passwordDataLength=incomingPasswordLength; - - if (*passwordDataLength>0) - memcpy(passwordData, incomingPassword, *passwordDataLength); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Call this to connect to the specified host (ip or domain name) and server port. -// Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. Calling both acts as a true peer. -// This is a non-blocking connection. You know the connection is successful when IsConnected() returns true -// or receive gets a packet with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. If the connection is not -// successful, such as rejected connection or no response then neither of these things will happen. -// Requires that you first call Initialize -// -// Parameters: -// host: Either a dotted IP address or a domain name -// remotePort: Which port to connect to on the remote machine. -// passwordData: A data block that must match the data block on the server. This can be just a password, or can be a stream of data -// passwordDataLength: The length in bytes of passwordData -// -// Returns: -// True on successful initiation. False on incorrect parameters, internal error, or too many existing peers -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -ConnectionAttemptResult RakPeer::Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime ) -{ - // If endThreads is true here you didn't call Startup() first. - if ( host == 0 || endThreads || connectionSocketIndex>=socketList.Size() ) - return INVALID_PARAMETER; - - RakAssert(remotePort!=0); - - connectionSocketIndex=GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - - if (passwordDataLength>255) - passwordDataLength=255; - - if (passwordData==0) - passwordDataLength=0; - - // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? - // It won't overflow at least because outgoingPasswordLength is an unsigned char -// if (passwordDataLength>0) -// memcpy(outgoingPassword, passwordData, passwordDataLength); -// outgoingPasswordLength=(unsigned char) passwordDataLength; - - // 04/02/09 - Can't remember why I disabled connecting to self, but it seems to work - // Connecting to ourselves in the same instance of the program? -// if ( ( strcmp( host, "127.0.0.1" ) == 0 || strcmp( host, "0.0.0.0" ) == 0 ) && remotePort == mySystemAddress[0].port ) -// return false; - - return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, connectionSocketIndex, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -ConnectionAttemptResult RakPeer::ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSocket2* socket, PublicKey *publicKey, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime) -{ - if ( host == 0 || endThreads || socket == 0 ) - return INVALID_PARAMETER; - - if (passwordDataLength>255) - passwordDataLength=255; - - if (passwordData==0) - passwordDataLength=0; - - return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, 0, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime, socket ); - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Stops the network threads and close all connections. Multiple calls are ok. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::Shutdown( unsigned int blockDuration, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority ) -{ - unsigned i,j; - bool anyActive; - MafiaNet::TimeMS startWaitingTime; -// SystemAddress systemAddress; - MafiaNet::TimeMS time; - //unsigned short systemListSize = remoteSystemListSize; // This is done for threading reasons - unsigned int systemListSize = maximumNumberOfPeers; - - ++shutdownGeneration; - - if ( blockDuration > 0 ) - { - for ( i = 0; i < systemListSize; i++ ) - { - // remoteSystemList in user thread - if (remoteSystemList[i].isActive) - NotifyAndFlagForShutdown(remoteSystemList[i].systemAddress, false, orderingChannel, disconnectionNotificationPriority); - } - - time = MafiaNet::GetTimeMS(); - startWaitingTime = time; - // Nothing else advances a manually updated peer (or its clock, under a - // test's virtual time): flush the notifications once and stop. - if (manualUpdate) - RunManualUpdate(); - while ( !manualUpdate && time - startWaitingTime < blockDuration ) - { - anyActive=false; - for (j=0; j < systemListSize; j++) - { - // remoteSystemList in user thread - if (remoteSystemList[j].isActive) - { - anyActive=true; - break; - } - } - - // If this system is out of packets to send, then stop waiting - if ( anyActive==false ) - break; - - // This will probably cause the update thread to run which will probably - // send the disconnection notification - - RakSleep(15); - time = MafiaNet::GetTimeMS(); - } - } - for (i=0; i < pluginListTS.Size(); i++) - { - pluginListTS[i]->OnRakPeerShutdown(); - } - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginListNTS[i]->OnRakPeerShutdown(); - } - - quitAndDataEvents.SetEvent(); - - endThreads = true; - -// MafiaNet::TimeMS timeout; -#if RAKPEER_USER_THREADED!=1 - - for (i=0; i < socketList.Size(); i++) - { - if (socketList[i]->IsBerkleySocket()) - { - ((RNS2_Berkley *)socketList[i])->SignalStopRecvPollingThread(); - } - } - - /* - // Get recvfrom to unblock - for (i=0; i < socketList.Size(); i++) - { - if (SocketLayer::SendTo(socketList[i], (const char*) &i,1,socketList[i]->GetBoundAddress(), _FILE_AND_LINE_)!=0) - break; - } - */ - - while ( isMainLoopThreadActive ) - { - RakSleep(15); - } - - activeSystemListSize = 0; - - /* - timeout = MafiaNet::GetTimeMS()+1000; - while ( isRecvFromLoopThreadActive.GetValue()>0 && MafiaNet::GetTimeMS()GetBoundAddress(), _FILE_AND_LINE_); - } - - RakSleep(30); - } - */ - - for (i=0; i < socketList.Size(); i++) - { - if (socketList[i]->IsBerkleySocket()) - { - ((RNS2_Berkley *)socketList[i])->BlockOnStopRecvPollingThread(); - } - } - - -#endif // RAKPEER_USER_THREADED!=1 - -// char c=0; -// unsigned int socketIndex; - // remoteSystemList in Single thread - for ( i = 0; i < systemListSize; i++ ) - { - // Reserve this reliability layer for ourselves - remoteSystemList[ i ].isActive = false; - - // Remove any remaining packets - RakAssert(remoteSystemList[ i ].MTUSize <= MAXIMUM_MTU_SIZE); - remoteSystemList[ i ].reliabilityLayer.Reset(false, remoteSystemList[ i ].MTUSize, false); - remoteSystemList[ i ].rakNetSocket = 0; - ClearDisconnectReason(&remoteSystemList[ i ]); - ClearSessionConfig(&remoteSystemList[ i ]); - } - - - // Setting maximumNumberOfPeers to 0 allows remoteSystemList to be reallocated in Initialize. - // Setting remoteSystemListSize prevents threads from accessing the reliability layer - maximumNumberOfPeers = 0; - //remoteSystemListSize = 0; - - // Free any packets the user didn't deallocate - packetReturnMutex.Lock(); - for (i=0; i < packetReturnQueue.Size(); i++) - DeallocatePacket(packetReturnQueue[i]); - packetReturnQueue.Clear(_FILE_AND_LINE_); - packetReturnMutex.Unlock(); - // Packets still out live in the pool's pages: a Shutdown() from inside Receive's dispatch, - // or from code handling a packet Receive returned, must not free the packet under it. The - // pages are reused by the next session and freed by a later Shutdown() or the destructor. - packetAllocationPoolMutex.Lock(); - if (packetsOutstanding == 0) - packetAllocationPool.Clear(_FILE_AND_LINE_); - packetAllocationPoolMutex.Unlock(); - - /* - if (isRecvFromLoopThreadActive.GetValue()>0) - { - timeout = MafiaNet::GetTimeMS()+1000; - while ( isRecvFromLoopThreadActive.GetValue()>0 && MafiaNet::GetTimeMS() addresses; - DataStructures::List guids; - GetSystemList(addresses, guids); - if (remoteSystems) - { - unsigned short i; - for (i=0; i < *numberOfSystems && i < addresses.Size(); i++) - remoteSystems[i]=addresses[i]; - *numberOfSystems=i; - } - else - { - *numberOfSystems=(unsigned short) addresses.Size(); - } - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::GetNextSendReceipt(void) -{ - sendReceiptSerialMutex.Lock(); - uint32_t retVal = sendReceiptSerial; - sendReceiptSerialMutex.Unlock(); - return retVal; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::IncrementNextSendReceipt(void) -{ - sendReceiptSerialMutex.Lock(); - uint32_t returned = sendReceiptSerial; - if (++sendReceiptSerial==0) - sendReceiptSerial=1; - sendReceiptSerialMutex.Unlock(); - return returned; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sends a block of data to the specified system that you are connected to. -// This function only works while the client is connected (Use the Connect function). -// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM -// -// Parameters: -// data: The block of data to send -// length: The size in bytes of the data to send -// bitStream: The bitstream to send -// priority: What priority level to send on. -// reliability: How reliability to send this data -// orderingChannel: When using ordered or sequenced packets, what channel to order these on. -// - Packets are only ordered relative to other packets on the same stream -// systemAddress: Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none -// broadcast: True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. -// Returns: -// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// A peer mid-session-handshake has not been reported to the application, so the application must not be -// able to address it. Checked here rather than in SendImmediate because the handshake's own messages go -// through SendImmediate directly and must still reach that peer. -bool RakPeer::IsExchangingSessionData( const AddressOrGUID systemIdentifier ) -{ - if (remoteSystemList==0) - return false; - - int index; - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); - else - index = GetIndexFromGuid(systemIdentifier.rakNetGuid); - - return index!=-1 && remoteSystemList[index].isActive && - remoteSystemList[index].connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::Send( const char *data, const int length, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) -{ -#ifdef _DEBUG - RakAssert( data && length > 0 ); -#endif - RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); - RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); - RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); - - if ( data == 0 || length < 0 ) - return 0; - - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - if ( broadcast == false && systemIdentifier.IsUndefined()) - return 0; - - if ( broadcast == false && IsExchangingSessionData(systemIdentifier) ) - return 0; - - uint32_t usedSendReceipt; - if (forceReceiptNumber!=0) - usedSendReceipt=forceReceiptNumber; - else - usedSendReceipt=IncrementNextSendReceipt(); - - if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) - { - SendLoopback(data,length); - - if (reliability>=MafiaNet::Reliability::UnreliableWithAckReceipt) - { - char buff[5]; - buff[0]=ID_SND_RECEIPT_ACKED; - sendReceiptSerialMutex.Lock(); - memcpy(buff+1, &sendReceiptSerial, 4); - sendReceiptSerialMutex.Unlock(); - SendLoopback( buff, 5 ); - } - - return usedSendReceipt; - } - - SendBuffered(data, length*8, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); - - return usedSendReceipt; -} - -void RakPeer::SendLoopback( const char *data, const int length ) -{ - if ( data == 0 || length < 0 ) - return; - - Packet *packet = AllocPacket(length, _FILE_AND_LINE_); - memcpy(packet->data, data, length); - packet->systemAddress = GetLoopbackAddress(); - packet->guid=myGuid; - PushBackPacket(packet, false); -} - -uint32_t RakPeer::Send( const MafiaNet::BitStream * bitStream, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) -{ -#ifdef _DEBUG - RakAssert( bitStream->GetNumberOfBytesUsed() > 0 ); -#endif - - RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); - RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); - RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); - - if ( bitStream->GetNumberOfBytesUsed() == 0 ) - return 0; - - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - if ( broadcast == false && systemIdentifier.IsUndefined() ) - return 0; - - if ( broadcast == false && IsExchangingSessionData(systemIdentifier) ) - return 0; - - uint32_t usedSendReceipt; - if (forceReceiptNumber!=0) - usedSendReceipt=forceReceiptNumber; - else - usedSendReceipt=IncrementNextSendReceipt(); - - if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) - { - SendLoopback((const char*) bitStream->GetData(),bitStream->GetNumberOfBytesUsed()); - if (reliability>=MafiaNet::Reliability::UnreliableWithAckReceipt) - { - char buff[5]; - buff[0]=ID_SND_RECEIPT_ACKED; - sendReceiptSerialMutex.Lock(); - memcpy(buff+1, &sendReceiptSerial,4); - sendReceiptSerialMutex.Unlock(); - SendLoopback( buff, 5 ); - } - return usedSendReceipt; - } - - // Sends need to be buffered and processed in the update thread because the systemAddress associated with the reliability layer can change, - // from that thread, resulting in a send to the wrong player! While I could mutex the systemAddress, that is much slower than doing this - SendBuffered((const char*)bitStream->GetData(), bitStream->GetNumberOfBitsUsed(), priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); - - - return usedSendReceipt; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Sends multiple blocks of data, concatenating them automatically. -// -// This is equivalent to: -// MafiaNet::BitStream bs; -// bs.WriteAlignedBytes(block1, blockLength1); -// bs.WriteAlignedBytes(block2, blockLength2); -// bs.WriteAlignedBytes(block3, blockLength3); -// Send(&bs, ...) -// -// This function only works while connected -// \param[in] data An array of pointers to blocks of data -// \param[in] lengths An array of integers indicating the length of each block of data -// \param[in] numParameters Length of the arrays data and lengths -// \param[in] priority What priority level to send on. See packet_priority.h -// \param[in] reliability How reliability to send this data. See packet_priority.h -// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream -// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none -// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. -// \return False if we are not connected to the specified recipient. True otherwise -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::SendList( const char **data, const int *lengths, const int numParameters, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) -{ -#ifdef _DEBUG - RakAssert( data ); -#endif - - if ( data == 0 || lengths == 0 ) - return 0; - - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - if (numParameters==0) - return 0; - - if (lengths==0) - return 0; - - if ( broadcast == false && systemIdentifier.IsUndefined() ) - return 0; - - if ( broadcast == false && IsExchangingSessionData(systemIdentifier) ) - return 0; - - uint32_t usedSendReceipt; - if (forceReceiptNumber!=0) - usedSendReceipt=forceReceiptNumber; - else - usedSendReceipt=IncrementNextSendReceipt(); - - SendBufferedList(data, lengths, numParameters, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); - - return usedSendReceipt; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Gets a packet from the incoming packet queue. Use DeallocatePacket to deallocate the packet after you are done with it. -// Check the Packet struct at the top of CoreNetworkStructures.h for the format of the struct -// -// Returns: -// 0 if no packets are waiting to be handled, otherwise an allocated packet -// If the client is not active this will also return 0, as all waiting packets are flushed when the client is Disconnected -// This also updates all memory blocks associated with synchronized memory and distributed objects -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -Packet* RakPeer::Receive( void ) -{ - if ( !( IsActive() ) ) - return 0; - - MafiaNet::Packet *packet; -// Packet **threadPacket; - PluginReceiveResult pluginResult; - - int offset; - unsigned int i; - - // User should call RunUpdateCycle and RunRecvFromOnce to do this commented code - /* -#if RAKPEER_SINGLE_THREADED==1 - RakPeer::RecvFromStruct *recvFromStruct; - for (i=0; i < socketList.Size(); i++) - { - for(;;) - { - recvFromStruct=bufferedPackets.Allocate( _FILE_AND_LINE_ ); - recvFromStruct->s=socketList[i]->s; - recvFromStruct->extraSocketOptions=socketList[i]->extraSocketOptions; - SocketLayer::RecvFromBlocking( - recvFromStruct->s, this, recvFromStruct->extraSocketOptions, recvFromStruct->data, &recvFromStruct->bytesRead, &recvFromStruct->systemAddress, &recvFromStruct->timeRead); - if (recvFromStruct->bytesRead<=0) - { - bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); - break; - } - else - { - RakAssert(recvFromStruct->systemAddress.GetPort()); - bufferedPackets.Push(recvFromStruct); - } - } - } - - BitStream updateBitStream( MAXIMUM_MTU_SIZE -#if LIBCAT_SECURITY==1 - + cat::AuthenticatedEncryption::OVERHEAD_BYTES -#endif - ); - RunUpdateCycle(0, 0, updateBitStream); -#endif - */ - - for (i=0; i < pluginListTS.Size(); i++) - { - pluginListTS[i]->Update(); - } - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginListNTS[i]->Update(); - } - - do - { - packetReturnMutex.Lock(); - if (packetReturnQueue.IsEmpty()) - packet=0; - else - packet = packetReturnQueue.Pop(); - packetReturnMutex.Unlock(); - if (packet==0) - return 0; - -// unsigned char msgId; - if ( ( packet->length >= sizeof(unsigned char) + sizeof(MafiaNet::Time ) ) && - ( (unsigned char) packet->data[ 0 ] == ID_TIMESTAMP ) ) - { - offset = sizeof(unsigned char); - ShiftIncomingTimestamp( packet->data + offset, packet->systemAddress ); -// msgId=packet->data[sizeof(unsigned char) + sizeof( MafiaNet::Time )]; - } -// else - // msgId=packet->data[0]; - - // Some locally generated packets need to be processed by plugins, for example ID_FCM2_NEW_HOST - // The plugin itself should intercept these messages generated remotely -// if (packet->wasGeneratedLocally) -// return packet; - - - // A callback may shut the peer down. The plugins have been reset by then and the packet - // belongs to a closed session, so it goes no further and Receive returns nothing. - const unsigned int generation = shutdownGeneration; - - CallPluginCallbacks(pluginListTS, packet); - CallPluginCallbacks(pluginListNTS, packet); - if (shutdownGeneration != generation) - { - DeallocatePacket( packet ); - return 0; - } - - for (i=0; i < pluginListTS.Size(); i++) - { - pluginResult=pluginListTS[i]->OnReceive(packet); - if (shutdownGeneration != generation) - { - if (pluginResult!=RR_STOP_PROCESSING) - DeallocatePacket( packet ); - return 0; - } - if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) - { - DeallocatePacket( packet ); - packet=0; // Will do the loop again and get another packet - break; // break out of the enclosing for - } - else if (pluginResult==RR_STOP_PROCESSING) - { - packet=0; - break; - } - } - - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginResult=pluginListNTS[i]->OnReceive(packet); - if (shutdownGeneration != generation) - { - if (pluginResult!=RR_STOP_PROCESSING) - DeallocatePacket( packet ); - return 0; - } - if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) - { - DeallocatePacket( packet ); - packet=0; // Will do the loop again and get another packet - break; // break out of the enclosing for - } - else if (pluginResult==RR_STOP_PROCESSING) - { - packet=0; - break; - } - } - - } while(packet==0); - -#ifdef _DEBUG - RakAssert( packet->data ); -#endif - - return packet; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Call this to deallocate a packet returned by Receive -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DeallocatePacket( Packet *packet ) -{ - if ( packet == 0 ) - return; - - if (packet->deleteData) - { - rakFree_Ex(packet->data, _FILE_AND_LINE_ ); - packet->~Packet(); - packetAllocationPoolMutex.Lock(); - packetAllocationPool.Release(packet,_FILE_AND_LINE_); - --packetsOutstanding; - packetAllocationPoolMutex.Unlock(); - } - else - { - rakFree_Ex(packet, _FILE_AND_LINE_ ); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Return the total number of connections we are allowed -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetMaximumNumberOfPeers( void ) const -{ - return maximumNumberOfPeers; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). -// -// Parameters: -// target: Which connection to close -// sendDisconnectionNotification: True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. -// channel: If blockDuration > 0, the disconnect packet will be sent on this channel -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority, const MafiaNet::BitStream *reasonData ) -{ - /* - // This only be called from the user thread, for the user shutting down. - // From the network thread, this should occur because of ID_DISCONNECTION_NOTIFICATION and ID_CONNECTION_LOST - unsigned j; - for (j=0; j < messageHandlerList.Size(); j++) - { - messageHandlerList[j]->OnClosedConnection( - target.systemAddress==UNASSIGNED_SYSTEM_ADDRESS ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress, - target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid, - LCR_CLOSED_BY_USER); - } - */ - - const SystemAddress address = (target.systemAddress == UNASSIGNED_SYSTEM_ADDRESS) ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress; - int remoteSystemListIndex = GetIndexFromSystemAddress(address); - - // Resolve the socket to close on WITHOUT assuming a valid slot index. - // GetIndexFromSystemAddress returns -1 when the target isn't in the list; never - // coerce that to 0 — reading remoteSystemList[0] would crash if the list is - // unallocated, or target an unrelated peer's slot. When the index is valid use - // that slot's socket (it may itself be null during rapid connect/disconnect - // churn). Either way, fall back to the primary socket — the same pattern used by - // the BCS_CLOSE_CONNECTION path below. With no socket at all there is nothing to - // close, so bail out. - RakNetSocket2 *closeSocket = (remoteSystemListIndex != -1) ? remoteSystemList[remoteSystemListIndex].rakNetSocket : nullptr; - if (closeSocket == nullptr && socketList.Size() > 0) - closeSocket = socketList[0]; - if (closeSocket == nullptr) - return; - CloseConnectionInternal2(target, sendDisconnectionNotification, false, orderingChannel, disconnectionNotificationPriority, *closeSocket, reasonData); - - // 12/14/09 Return ID_CONNECTION_LOST when calling CloseConnection with sendDisconnectionNotification==false, elsewise it is never returned - if (sendDisconnectionNotification==false && GetConnectionState(target)==IS_CONNECTED) - { - Packet *packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection - packet->guid = target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid; - packet->systemAddress = address; - packet->systemAddress.systemIndex = static_cast(remoteSystemListIndex == -1 ? 0 : remoteSystemListIndex); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - packet->wasGeneratedLocally=true; // else processed twice - AddPacketToProducer(packet); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Cancel a pending connection attempt -// If we are already connected, the connection stays open -// \param[in] target Which system to cancel -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::CancelConnectionAttempt( const SystemAddress target ) -{ - unsigned int i; - - // Cancel pending connection attempt, if there is one - i=0; - requestedConnectionQueueMutex.Lock(); - while (i < requestedConnectionQueue.Size()) - { - if (requestedConnectionQueue[i]->systemAddress==target) - { -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Deleting requestedConnectionQueue %i client_handshake %x\n", i, requestedConnectionQueue[ i ]->client_handshake); - MafiaNet::OP_DELETE(requestedConnectionQueue[i]->client_handshake, _FILE_AND_LINE_ ); -#endif - MafiaNet::OP_DELETE(requestedConnectionQueue[i], _FILE_AND_LINE_ ); - requestedConnectionQueue.RemoveAtIndex(i); - break; - } - else - i++; - } - requestedConnectionQueueMutex.Unlock(); - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -ConnectionState RakPeer::GetConnectionState(const AddressOrGUID systemIdentifier) -{ - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - unsigned int i=0; - requestedConnectionQueueMutex.Lock(); - for (; i < requestedConnectionQueue.Size(); i++) - { - if (requestedConnectionQueue[i]->systemAddress==systemIdentifier.systemAddress) - { - requestedConnectionQueueMutex.Unlock(); - return IS_PENDING; - } - } - requestedConnectionQueueMutex.Unlock(); - } - - int index; - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); - } - else - { - index = GetIndexFromGuid(systemIdentifier.rakNetGuid); - } - - if (index==-1) - return IS_NOT_CONNECTED; - - if (remoteSystemList[index].isActive==false) - return IS_DISCONNECTED; - - switch (remoteSystemList[index].connectMode) - { - case RemoteSystemStruct::DISCONNECT_ASAP: - return IS_DISCONNECTING; - case RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY: - return IS_SILENTLY_DISCONNECTING; - case RemoteSystemStruct::DISCONNECT_ON_NO_ACK: - return IS_DISCONNECTING; - case RemoteSystemStruct::REQUESTED_CONNECTION: - return IS_CONNECTING; - case RemoteSystemStruct::HANDLING_CONNECTION_REQUEST: - return IS_CONNECTING; - case RemoteSystemStruct::UNVERIFIED_SENDER: - return IS_CONNECTING; - case RemoteSystemStruct::EXCHANGING_SESSION_DATA: - // Transport is up but the session handshake has not completed, so the application has not been - // told this connection exists yet. - return IS_CONNECTING; - case RemoteSystemStruct::CONNECTED: - return IS_CONNECTED; - default: - return IS_NOT_CONNECTED; - } -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Given a systemAddress, returns an index from 0 to the maximum number of players allowed - 1. -// -// Parameters -// systemAddress - The systemAddress to search for -// -// Returns -// An integer from 0 to the maximum number of peers -1, or -1 if that player is not found -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress ) const -{ - return GetIndexFromSystemAddress(systemAddress, false); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// This function is only useful for looping through all players. -// -// Parameters -// index - an integer between 0 and the maximum number of players allowed - 1. -// -// Returns -// A valid systemAddress or UNASSIGNED_SYSTEM_ADDRESS if no such player at that index -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetSystemAddressFromIndex( unsigned int index ) -{ - // remoteSystemList in user thread - //if ( index >= 0 && index < remoteSystemListSize ) - if ( index < maximumNumberOfPeers ) - if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail - return remoteSystemList[ index ].systemAddress; - - return UNASSIGNED_SYSTEM_ADDRESS; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Same as GetSystemAddressFromIndex but returns RakNetGUID -// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. -// \return The RakNetGUID -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakNetGUID RakPeer::GetGUIDFromIndex( unsigned int index ) -{ - // remoteSystemList in user thread - //if ( index >= 0 && index < remoteSystemListSize ) - if ( index < maximumNumberOfPeers ) - if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail - return remoteSystemList[ index ].guid; - - return UNASSIGNED_RAKNET_GUID; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient -// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system -// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the list will match the size of the \a guids list. -// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const -{ - addresses.Clear(false, _FILE_AND_LINE_); - guids.Clear(false, _FILE_AND_LINE_); - - if ( remoteSystemList == 0 || endThreads == true ) - return; - - unsigned int i; - for (i=0; i < activeSystemListSize; i++) - { - if ((activeSystemList[i])->isActive && - (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED) - { - addresses.Push((activeSystemList[i])->systemAddress, _FILE_AND_LINE_ ); - guids.Push((activeSystemList[i])->guid, _FILE_AND_LINE_ ); - } - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Bans an IP from connecting. Banned IPs persist between connections. -// -// Parameters -// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban -// All IP addresses starting with 128.0.0 -// milliseconds - how many ms for a temporary ban. Use 0 for a permanent ban -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AddToBanList( const char *IP, MafiaNet::TimeMS milliseconds ) -{ - unsigned index; - MafiaNet::TimeMS time = MafiaNet::GetTimeMS(); - - if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) - return ; - - // If this guy is already in the ban list, do nothing - index = 0; - - banListMutex.Lock(); - - for ( ; index < banList.Size(); index++ ) - { - if ( strcmp( IP, banList[ index ]->IP ) == 0 ) - { - // Already in the ban list. Just update the time - if (milliseconds==0) - banList[ index ]->timeout=0; // Infinite - else - banList[ index ]->timeout=time+milliseconds; - banListMutex.Unlock(); - return; - } - } - - banListMutex.Unlock(); - - BanStruct *banStruct = MafiaNet::OP_NEW( _FILE_AND_LINE_ ); - banStruct->IP = (char*) rakMalloc_Ex( 16, _FILE_AND_LINE_ ); - if (milliseconds==0) - banStruct->timeout=0; // Infinite - else - banStruct->timeout=time+milliseconds; - strcpy_s( banStruct->IP, 16, IP ); - banListMutex.Lock(); - banList.Insert( banStruct, _FILE_AND_LINE_ ); - banListMutex.Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Allows a previously banned IP to connect. -// -// Parameters -// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban -// All IP addresses starting with 128.0.0 -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RemoveFromBanList( const char *IP ) -{ - unsigned index; - BanStruct *temp; - - if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) - return ; - - index = 0; - temp=0; - - banListMutex.Lock(); - - for ( ; index < banList.Size(); index++ ) - { - if ( strcmp( IP, banList[ index ]->IP ) == 0 ) - { - temp = banList[ index ]; - banList[ index ] = banList[ banList.Size() - 1 ]; - banList.RemoveAtIndex( banList.Size() - 1 ); - break; - } - } - - banListMutex.Unlock(); - - if (temp) - { - rakFree_Ex(temp->IP, _FILE_AND_LINE_ ); - MafiaNet::OP_DELETE(temp, _FILE_AND_LINE_); - } - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Allows all previously banned IPs to connect. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearBanList( void ) -{ - unsigned index; - index = 0; - banListMutex.Lock(); - - for ( ; index < banList.Size(); index++ ) - { - rakFree_Ex(banList[ index ]->IP, _FILE_AND_LINE_ ); - MafiaNet::OP_DELETE(banList[ index ], _FILE_AND_LINE_); - } - - banList.Clear(false, _FILE_AND_LINE_); - - banListMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetLimitIPConnectionFrequency(bool b) -{ - limitConnectionFrequencyFromTheSameIP=b; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Determines if a particular IP is banned. -// -// Parameters -// IP - Complete dotted IP address -// -// Returns -// True if IP matches any IPs in the ban list, accounting for any wildcards. -// False otherwise. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsBanned( const char *IP ) -{ - unsigned banListIndex, characterIndex; - MafiaNet::TimeMS time; - BanStruct *temp; - - if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) - return false; - - banListIndex = 0; - - if ( banList.Size() == 0 ) - return false; // Skip the mutex if possible - - time = MafiaNet::GetTimeMS(); - - banListMutex.Lock(); - - while ( banListIndex < banList.Size() ) - { - if (banList[ banListIndex ]->timeout>0 && banList[ banListIndex ]->timeoutIP, _FILE_AND_LINE_ ); - MafiaNet::OP_DELETE(temp, _FILE_AND_LINE_); - } - else - { - characterIndex = 0; - - for(;;) - { - if ( banList[ banListIndex ]->IP[ characterIndex ] == IP[ characterIndex ] ) - { - // Equal characters - - if ( IP[ characterIndex ] == 0 ) - { - banListMutex.Unlock(); - // End of the string and the strings match - - return true; - } - - characterIndex++; - } - - else - { - if ( banList[ banListIndex ]->IP[ characterIndex ] == 0 || IP[ characterIndex ] == 0 ) - { - // End of one of the strings - break; - } - - // Characters do not match - if ( banList[ banListIndex ]->IP[ characterIndex ] == '*' ) - { - banListMutex.Unlock(); - - // Domain is banned. - return true; - } - - // Characters do not match and it is not a * - break; - } - } - - banListIndex++; - } - } - - banListMutex.Unlock(); - - // No match found. - return false; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Send a ping to the specified connected system. -// -// Parameters: -// target - who to ping -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::Ping( const SystemAddress target ) -{ - PingInternal(target, false, MafiaNet::Reliability::Unreliable); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Send a ping to the specified unconnected system. -// The remote system, if it is Initialized, will respond with ID_UNCONNECTED_PONG. -// The final ping time will be encoded in the following sizeof(MafiaNet::TimeMS) bytes. (Default is 4 bytes - See __GET_TIME_64BIT in types.h -// -// Parameters: -// host: Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. -// remotePort: Which port to connect to on the remote machine. -// onlyReplyOnAcceptingConnections: Only request a reply if the remote system has open connections -// connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex ) -{ - if ( host == 0 ) - return false; - - // If this assert hits then Startup wasn't called or the call failed. - RakAssert(connectionSocketIndex < socketList.Size()); - -// if ( IsActive() == false ) -// return; - - MafiaNet::BitStream bitStream( sizeof(unsigned char) + sizeof(MafiaNet::Time) ); - if ( onlyReplyOnAcceptingConnections ) - bitStream.Write((MessageID)ID_UNCONNECTED_PING_OPEN_CONNECTIONS); - else - bitStream.Write((MessageID)ID_UNCONNECTED_PING); - - bitStream.Write(MafiaNet::GetTime()); - - bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - - bitStream.Write(GetMyGUID()); - - // No timestamp for 255.255.255.255 - unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - /* - - SystemAddress systemAddress; - systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); - systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); - - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( socketList[realIndex], (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - */ - - RNS2_SendParameters bsp; - bsp.data = (char*) bitStream.GetData() ; - bsp.length = bitStream.GetNumberOfBytesUsed(); - bsp.systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); - if (bsp.systemAddress==UNASSIGNED_SYSTEM_ADDRESS) - return false; - bsp.systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), bsp.systemAddress); - socketList[realIndex]->Send(&bsp, _FILE_AND_LINE_); - - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the average of all ping times read for a specified target -// -// Parameters: -// target - whose time to read -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetAveragePing( const AddressOrGUID systemIdentifier ) -{ - int sum, quantity; - RemoteSystemStruct *remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); - - if ( remoteSystem == 0 ) - return -1; - - for ( sum = 0, quantity = 0; quantity < PING_TIMES_ARRAY_SIZE; quantity++ ) - { - if ( remoteSystem->pingAndClockDifferential[ quantity ].pingTime == 65535 ) - break; - else - sum += remoteSystem->pingAndClockDifferential[ quantity ].pingTime; - } - - if ( quantity > 0 ) - return sum / quantity; - else - return -1; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the last ping time read for the specific player or -1 if none read yet -// -// Parameters: -// target - whose time to read -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetLastPing( const AddressOrGUID systemIdentifier ) const -{ - RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); - - if ( remoteSystem == 0 ) - return -1; - -// return (int)(remoteSystem->reliabilityLayer.GetAckPing()/(MafiaNet::TimeUS)1000); - - if ( remoteSystem->pingAndClockDifferentialWriteIndex == 0 ) - return remoteSystem->pingAndClockDifferential[ PING_TIMES_ARRAY_SIZE - 1 ].pingTime; - else - return remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex - 1 ].pingTime; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the lowest ping time read or -1 if none read yet -// -// Parameters: -// target - whose time to read -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetLowestPing( const AddressOrGUID systemIdentifier ) const -{ - RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); - - if ( remoteSystem == 0 ) - return -1; - - return remoteSystem->lowestPing; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Ping the remote systems every so often. This is off by default -// This will work anytime -// -// Parameters: -// doPing - True to start occasional pings. False to stop them. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetOccasionalPing( bool doPing ) -{ - occasionalPing = doPing; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -/// Return the clock difference between your system and the specified system -/// Subtract the time from a time returned by the remote system to get that time relative to your own system -/// Returns 0 if the system is unknown -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -MafiaNet::Time RakPeer::GetClockDifferential( const AddressOrGUID systemIdentifier ) -{ - RemoteSystemStruct *remoteSystem = GetRemoteSystem(systemIdentifier, false, false); - if (remoteSystem == 0) - return 0; - return GetClockDifferentialInt(remoteSystem); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -MafiaNet::Time RakPeer::GetClockDifferentialInt(RemoteSystemStruct *remoteSystem) const -{ - int counter, lowestPingSoFar; - MafiaNet::Time clockDifferential; - - lowestPingSoFar = 65535; - - clockDifferential = 0; - - for ( counter = 0; counter < PING_TIMES_ARRAY_SIZE; counter++ ) - { - if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime == 65535 ) - break; - - if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime < lowestPingSoFar ) - { - clockDifferential = remoteSystem->pingAndClockDifferential[ counter ].clockDifferential; - lowestPingSoFar = remoteSystem->pingAndClockDifferential[ counter ].pingTime; - } - } - - return clockDifferential; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Length should be under 400 bytes, as a security measure against flood attacks -// Sets the data to send with an (LAN server discovery) /(offline ping) response -// See the Ping sample project for how this is used. -// data: a block of data to store, or 0 for none -// length: The length of data in bytes, or 0 for none -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetOfflinePingResponse( const char *data, const unsigned int length ) -{ - RakAssert(length < 400); - - rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); - offlinePingResponse.Reset(); - - if ( data && length > 0 ) - offlinePingResponse.Write( data, length ); - - rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns pointers to a copy of the data passed to SetOfflinePingResponse -// \param[out] data A pointer to a copy of the data passed to \a SetOfflinePingResponse() -// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() -// \sa SetOfflinePingResponse -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetOfflinePingResponse( char **data, unsigned int *length ) -{ - rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); - *data = (char*) offlinePingResponse.GetData(); - *length = (int) offlinePingResponse.GetNumberOfBytesUsed(); - rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Sets the payload this peer offers during the session handshake. Opaque to MafiaNet. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetSessionConfig( const char *data, unsigned int length ) -{ - RakAssert(length <= MAXIMUM_SESSION_CONFIG_SIZE); - if (length > MAXIMUM_SESSION_CONFIG_SIZE) - length = MAXIMUM_SESSION_CONFIG_SIZE; - - rakPeerMutexes[ sessionConfig_Mutex ].Lock(); - sessionConfig.Reset(); - - if ( data && length > 0 ) - sessionConfig.Write( data, length ); - - rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetSessionConfig( char **data, unsigned int *length ) -{ - rakPeerMutexes[ sessionConfig_Mutex ].Lock(); - *data = (char*) sessionConfig.GetData(); - *length = (unsigned int) sessionConfig.GetNumberOfBytesUsed(); - rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -const char *RakPeer::GetRemoteSessionConfig( const AddressOrGUID systemIdentifier, unsigned int *length ) -{ - if (length) - *length = 0; - - int index; - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); - else - index = GetIndexFromGuid(systemIdentifier.rakNetGuid); - - if (index==-1 || remoteSystemList==0 || remoteSystemList[index].isActive==false) - return 0; - - if (length) - *length = remoteSystemList[index].remoteSessionConfigLength; - return (const char*) remoteSystemList[index].remoteSessionConfigData; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetSessionConfigInteractive( bool interactive ) -{ - sessionConfigInteractive = interactive; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AcceptSession( const AddressOrGUID systemIdentifier, const char *data, unsigned int length ) -{ - RakAssert(length <= MAXIMUM_SESSION_CONFIG_SIZE); - if (length > MAXIMUM_SESSION_CONFIG_SIZE) - length = MAXIMUM_SESSION_CONFIG_SIZE; - QueueSessionDecision(systemIdentifier, true, data, length); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RejectSession( const AddressOrGUID systemIdentifier, const char *reason ) -{ - unsigned int reasonLength = (reason && reason[0]) ? (unsigned int) strlen(reason) : 0; - // Bounded like every other payload that crosses the handshake. A reason is meant to be a short - // string, so an oversized one is a caller mistake rather than something to transmit. - RakAssert(reasonLength <= MAXIMUM_SESSION_CONFIG_SIZE); - if (reasonLength > MAXIMUM_SESSION_CONFIG_SIZE) - reasonLength = MAXIMUM_SESSION_CONFIG_SIZE; - QueueSessionDecision(systemIdentifier, false, reason, reasonLength); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetMaximumPendingSessions( unsigned short total, unsigned short perAddress ) -{ - maximumPendingSessions = total; - maximumPendingSessionsPerAddress = total != 0 ? perAddress : 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetSessionTimeout( MafiaNet::TimeMS timeMS ) -{ - sessionTimeout = timeMS; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendSessionStatus( const AddressOrGUID systemIdentifier, const char *data, unsigned int length ) -{ - RakAssert(length <= MAXIMUM_SESSION_CONFIG_SIZE); - if (length > MAXIMUM_SESSION_CONFIG_SIZE) - length = MAXIMUM_SESSION_CONFIG_SIZE; - QueueSessionCommand(systemIdentifier, BufferedCommandStruct::BCS_SESSION_STATUS, data, length); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Hand an application's accept/reject over to the network thread. Applying it here would send on the -// connection and mutate connectMode from the user thread, which the rest of RakPeer never does. -void RakPeer::QueueSessionDecision( const AddressOrGUID systemIdentifier, bool accept, const char *data, unsigned int length ) -{ - QueueSessionCommand(systemIdentifier, accept ? BufferedCommandStruct::BCS_SESSION_ACCEPT : BufferedCommandStruct::BCS_SESSION_REJECT, data, length); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::QueueSessionCommand( const AddressOrGUID systemIdentifier, int command, const char *data, unsigned int length ) -{ - BufferedCommandStruct *bcs; - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->command = (decltype(bcs->command)) command; - bcs->systemIdentifier = systemIdentifier; - bcs->numberOfBitsToSend = BYTES_TO_BITS(length); - bcs->broadcast = false; - bcs->connectionMode = RemoteSystemStruct::NO_ACTION; - bcs->orderingChannel = 0; - bcs->priority = MafiaNet::Priority::Immediate; - bcs->reliability = MafiaNet::Reliability::ReliableOrdered; - bcs->data = 0; - if (data && length > 0) - { - bcs->data = (char*) rakMalloc_Ex(length, _FILE_AND_LINE_); - if (bcs->data) - memcpy(bcs->data, data, length); - else - bcs->numberOfBitsToSend = 0; - } - bufferedCommands.Push(bcs); - quitAndDataEvents.SetEvent(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Return the unique SystemAddress that represents you on the the network -// Note that unlike in previous versions, this is a struct and is not sequential -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetInternalID( const SystemAddress systemAddress, const int index ) const -{ - if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) - { - return ipList[index]; - } - else - { - -// SystemAddress returnValue; - RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, false, true ); - if (remoteSystem==0) - return UNASSIGNED_SYSTEM_ADDRESS; - - return remoteSystem->theirInternalSystemAddress[index]; - /* - sockaddr_in sa; - socklen_t len = sizeof(sa); - if (getsockname__(connectionSockets[remoteSystem->connectionSocketIndex], (sockaddr*)&sa, &len)!=0) - return UNASSIGNED_SYSTEM_ADDRESS; - returnValue.port=ntohs(sa.sin_port); - returnValue.binaryAddress=sa.sin_addr.s_addr; - return returnValue; -*/ - - - - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -/// \brief Sets your internal IP address, for platforms that do not support reading it, or to override a value -/// \param[in] systemAddress. The address to set. Use SystemAddress::FromString() if you want to use a dotted string -/// \param[in] index When you have multiple internal IDs, which index to set? -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetInternalID(SystemAddress systemAddress, int index) -{ - RakAssert(index >=0 && index < MAXIMUM_NUMBER_OF_INTERNAL_IDS); - ipList[index]=systemAddress; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Return the unique address identifier that represents you on the the network and is based on your external -// IP / port (the IP / port the specified player uses to communicate with you) -// Note that unlike in previous versions, this is a struct and is not sequential -// -// Parameters: -// target: Which remote system you are referring to for your external ID -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetExternalID( const SystemAddress target ) const -{ - unsigned i; - SystemAddress inactiveExternalId; - - inactiveExternalId=UNASSIGNED_SYSTEM_ADDRESS; - - if (target==UNASSIGNED_SYSTEM_ADDRESS) - return firstExternalID; - - // First check for active connection with this systemAddress - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].systemAddress == target ) - { - if ( remoteSystemList[ i ].isActive ) - return remoteSystemList[ i ].myExternalSystemAddress; - else if (remoteSystemList[ i ].myExternalSystemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - inactiveExternalId=remoteSystemList[ i ].myExternalSystemAddress; - } - } - - return inactiveExternalId; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -const RakNetGUID RakPeer::GetMyGUID(void) const -{ - return myGuid; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetMyBoundAddress(const int socketIndex) -{ - DataStructures::List sockets; - GetSockets( sockets ); - if (sockets.Size()>0) - return sockets[socketIndex]->GetBoundAddress(); - else - return UNASSIGNED_SYSTEM_ADDRESS; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -const RakNetGUID& RakPeer::GetGuidFromSystemAddress( const SystemAddress input ) const -{ - if (input==UNASSIGNED_SYSTEM_ADDRESS) - return myGuid; - - if (input.systemIndex!=(SystemIndex)-1 && input.systemIndexreliabilityLayer.SetTimeoutTime(timeMS); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -MafiaNet::TimeMS RakPeer::GetTimeoutTime( const SystemAddress target ) -{ - if (target==UNASSIGNED_SYSTEM_ADDRESS) - { - return defaultTimeoutTime; - } - else - { - RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( target, false, true ); - - if ( remoteSystem != 0 ) - return remoteSystem->reliabilityLayer.GetTimeoutTime(); - } - return defaultTimeoutTime; -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the current MTU size -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetMTUSize( const SystemAddress target ) const -{ - if (target!=UNASSIGNED_SYSTEM_ADDRESS) - { - RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(target, false, true); - if (rss) - return rss->MTUSize; - } - return defaultMTUSize; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the number of IP addresses we have -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetNumberOfAddresses( void ) -{ - if (IsActive() == false) { - FillIPList(); - } - - unsigned int i = 0; - while (i < MAXIMUM_NUMBER_OF_INTERNAL_IDS && ipList[i] != UNASSIGNED_SYSTEM_ADDRESS) - i++; // count assigned entries until the first unassigned one (end of list) - - return i; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns an IP address at index 0 to GetNumberOfAddresses-1 -// \param[in] index index into the list of IP addresses -// \return The local IP address at this index -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -const char* RakPeer::GetLocalIP( unsigned int index ) -{ - if (IsActive()==false) - { - // Fill out ipList structure - - FillIPList(); - - } - - - static char str[128]; - ipList[index].ToString(false,str,static_cast(128)); - return str; - - - - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Is this a local IP? -// \param[in] An IP address to check -// \return True if this is one of the IP addresses returned by GetLocalIP -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsLocalIP( const char *ip ) -{ - if (ip==0 || ip[0]==0) - return false; - - // #med - this should also check for "::1" here in IPv6 mode - if (strcmp(ip, "127.0.0.1")==0 || strcmp(ip, "localhost")==0) - return true; - - int num = GetNumberOfAddresses(); - int i; - for (i=0; i < num; i++) - { - if (strcmp(ip, GetLocalIP(i))==0) - return true; - } - - - - - return false; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary -// when connection to servers with multiple IP addresses -// -// Parameters: -// allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AllowConnectionResponseIPMigration( bool allow ) -{ - allowConnectionResponseIPMigration = allow; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sends a message ID_ADVERTISE_SYSTEM to the remote unconnected system. -// This will tell the remote system our external IP outside the LAN, and can be used for NAT punch through -// -// Requires: -// The sender and recipient must already be started via a successful call to Initialize -// -// host: Either a dotted IP address or a domain name -// remotePort: Which port to connect to on the remote machine. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex ) -{ - MafiaNet::BitStream bs; - bs.Write((MessageID)ID_ADVERTISE_SYSTEM); - bs.WriteAlignedBytes((const unsigned char*) data,dataLength); - return SendOutOfBand(host, remotePort, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), connectionSocketIndex ); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. -// ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived -// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. -// Defaults to 0 (never return this notification) -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetSplitMessageProgressInterval(int interval) -{ - RakAssert(interval>=0); - splitMessageProgressInterval=interval; - for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) - remoteSystemList[ i ].reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns what was passed to SetSplitMessageProgressInterval() -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetSplitMessageProgressInterval(void) const -{ - return splitMessageProgressInterval; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Set how long to wait before giving up on sending an unreliable message -// Useful if the network is clogged up. -// Set to 0 or less to never timeout. Defaults to 0. -// timeoutMS How many ms to wait before simply not sending an unreliable message. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetUnreliableTimeout(MafiaNet::TimeMS timeoutMS) -{ - unreliableTimeout=timeoutMS; - for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) - remoteSystemList[ i ].reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Send a message to host, with the IP socket option TTL set to 3 -// This message will not reach the host, but will open the router. -// Used for NAT-Punchthrough -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex ) -{ - char fakeData[2]; - fakeData[0]=0; - fakeData[1]=1; - unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - if (socketList[realIndex]->IsBerkleySocket()) - { - RNS2_SendParameters bsp; - bsp.data = (char*) fakeData; - bsp.length = 2; - bsp.systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); - bsp.systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); - bsp.ttl=ttl; - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*)bsp.data, BYTES_TO_BITS(bsp.length), bsp.systemAddress); - socketList[realIndex]->Send(&bsp, _FILE_AND_LINE_); - } -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Attatches a Plugin interface to run code automatically on message receipt in the Receive call -// -// \param messageHandler Pointer to a plugin to attach -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AttachPlugin( PluginInterface2 *plugin ) -{ - bool isNotThreadsafe = plugin->UsesReliabilityLayer(); - if (isNotThreadsafe) - { - if (pluginListNTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) - { - plugin->SetRakPeerInterface(this); - plugin->OnAttach(); - pluginListNTS.Insert(plugin, _FILE_AND_LINE_); - } - } - else - { - if (pluginListTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) - { - plugin->SetRakPeerInterface(this); - plugin->OnAttach(); - pluginListTS.Insert(plugin, _FILE_AND_LINE_); - } - } - - pluginListNetworkThreadMutex.Lock(); - if (pluginListNetworkThread.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) - pluginListNetworkThread.Insert(plugin, _FILE_AND_LINE_); - pluginListNetworkThreadMutex.Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Detaches a Plugin interface to run code automatically on message receipt -// -// \param messageHandler Pointer to a plugin to detach -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DetachPlugin( PluginInterface2 *plugin ) -{ - if (plugin==0) - return; - - unsigned int index; - - bool isNotThreadsafe = plugin->UsesReliabilityLayer(); - if (isNotThreadsafe) - { - index = pluginListNTS.GetIndexOf(plugin); - if (index!=MAX_UNSIGNED_LONG) - { - // Unordered list so delete from end for speed - pluginListNTS[index]=pluginListNTS[pluginListNTS.Size()-1]; - pluginListNTS.RemoveFromEnd(); - } - } - else - { - index = pluginListTS.GetIndexOf(plugin); - if (index!=MAX_UNSIGNED_LONG) - { - // Unordered list so delete from end for speed - pluginListTS[index]=pluginListTS[pluginListTS.Size()-1]; - pluginListTS.RemoveFromEnd(); - } - } - // Removed under the lock the update thread dispatches under, so once this returns the plugin - // is not inside OnReceiveNetworkThread and will not be called again. - pluginListNetworkThreadMutex.Lock(); - index = pluginListNetworkThread.GetIndexOf(plugin); - if (index!=MAX_UNSIGNED_LONG) - { - pluginListNetworkThread[index]=pluginListNetworkThread[pluginListNetworkThread.Size()-1]; - pluginListNetworkThread.RemoveFromEnd(); - } - pluginListNetworkThreadMutex.Unlock(); - - plugin->OnDetach(); - plugin->SetRakPeerInterface(0); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Put a packet back at the end of the receive queue in case you don't want to deal with it immediately -// -// packet The packet you want to push back. -// pushAtHead True to push the packet so that the next receive call returns it. False to push it at the end of the queue (obviously pushing it at the end makes the packets out of order) -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::PushBackPacket( Packet *packet, bool pushAtHead) -{ - if (packet==0) - return; - - unsigned i; - for (i=0; i < pluginListTS.Size(); i++) - pluginListTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); - - packetReturnMutex.Lock(); - if (pushAtHead) - packetReturnQueue.PushAtHead(packet,0,_FILE_AND_LINE_); - else - packetReturnQueue.Push(packet,_FILE_AND_LINE_); - packetReturnMutex.Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress) -{ - BufferedCommandStruct *bcs; - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->data = 0; - bcs->systemIdentifier.systemAddress=systemAddress; - bcs->systemIdentifier.rakNetGuid=guid; - bcs->command=BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS; - bufferedCommands.Push(bcs); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -Packet* RakPeer::AllocatePacket(unsigned dataSize) -{ - return AllocPacket(dataSize, _FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakNetSocket2* RakPeer::GetSocket( const SystemAddress target ) -{ - // Send a query to the thread to get the socket, and return when we got it - BufferedCommandStruct *bcs; - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; - bcs->systemIdentifier=target; - bcs->data=0; - bufferedCommands.Push(bcs); - - // Block up to one second to get the socket, although it should actually take virtually no time - SocketQueryOutput *sqo; - MafiaNet::TimeMS stopWaiting = MafiaNet::GetTimeMS()+1000; - DataStructures::List output; - while (manualUpdate || MafiaNet::GetTimeMS() < stopWaiting) - { - if (manualUpdate) - RunManualUpdate(); // answers the query on this thread - else if (isMainLoopThreadActive==false) - return 0; - else - RakSleep(0); - - sqo = socketQueryOutput.Pop(); - if (sqo) - { - output=sqo->sockets; - sqo->sockets.Clear(false, _FILE_AND_LINE_); - socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); - if (output.Size()) - return output[0]; - break; - } - } - return 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetSockets( DataStructures::List &sockets ) -{ - sockets.Clear(false, _FILE_AND_LINE_); - - // Send a query to the thread to get the socket, and return when we got it - BufferedCommandStruct *bcs; - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; - bcs->systemIdentifier=UNASSIGNED_SYSTEM_ADDRESS; - bcs->data=0; - bufferedCommands.Push(bcs); - - // Block up to one second to get the socket, although it should actually take virtually no time - SocketQueryOutput *sqo; -// RakNetSocket2* output; - for(;;) - { - if (manualUpdate) - RunManualUpdate(); // answers the query on this thread - else if (isMainLoopThreadActive==false) - return; - else - RakSleep(0); - - sqo = socketQueryOutput.Pop(); - if (sqo) - { - sockets=sqo->sockets; - sqo->sockets.Clear(false, _FILE_AND_LINE_); - socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); - return; - } - } - return; -} -void RakPeer::ReleaseSockets( DataStructures::List &sockets ) -{ - sockets.Clear(false,_FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Adds simulated ping and packet loss to the outgoing data flow. -// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and maxSendBPS value on each. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance) -{ -#ifndef _DEBUG - // unused parameters - (void)packetloss; - (void)minExtraPing; - (void)extraPingVariance; -#endif - -#ifdef _DEBUG - if (remoteSystemList) - { - unsigned short i; - for (i=0; i < maximumNumberOfPeers; i++) - //for (i=0; i < remoteSystemListSize; i++) - remoteSystemList[i].reliabilityLayer.ApplyNetworkSimulator(packetloss, minExtraPing, extraPingVariance); - } - - _packetloss=packetloss; - _minExtraPing=minExtraPing; - _extraPingVariance=extraPingVariance; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void RakPeer::SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond ) -{ - maxOutgoingBPS=maxBitsPerSecond; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns if you previously called ApplyNetworkSimulator -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsNetworkSimulatorActive( void ) -{ -#ifdef _DEBUG - return _packetloss>0 || _minExtraPing>0 || _extraPingVariance>0; -#else - return false; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::WriteOutOfBandHeader(MafiaNet::BitStream *bitStream) -{ - bitStream->Write((MessageID)ID_OUT_OF_BAND_INTERNAL); - bitStream->Write(myGuid); - bitStream->WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData) -{ - userUpdateThreadPtr=_userUpdateThreadPtr; - userUpdateThreadData=_userUpdateThreadData; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetSocketFactory(RakNetSocket2Factory *factory) -{ - RakAssert(IsActive() == false); - socketFactory = factory; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetManualUpdate(bool manual) -{ - RakAssert(IsActive() == false); - manualUpdate = manual; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RunManualUpdate() -{ - RakAssert(manualUpdate); - // The same steps as one pass of UpdateNetworkLoop. - if (userUpdateThreadPtr) - userUpdateThreadPtr(this, userUpdateThreadData); - BitStream updateBitStream( MAXIMUM_MTU_SIZE -#if LIBCAT_SECURITY==1 - + cat::AuthenticatedEncryption::OVERHEAD_BYTES -#endif - ); - RunUpdateCycle(updateBitStream); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetIncomingDatagramEventHandler( bool (*_incomingDatagramEventHandler)(RNS2RecvStruct *) ) -{ - incomingDatagramEventHandler=_incomingDatagramEventHandler; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex ) -{ - if ( IsActive() == false ) - return false; - - if (host==0 || host[0]==0) - return false; - - // If this assert hits then Startup wasn't called or the call failed. - RakAssert(connectionSocketIndex < socketList.Size()); - - // This is a security measure. Don't send data longer than this value - RakAssert(dataLength <= (MAX_OFFLINE_DATA_LENGTH + sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID))); - - if (host==0) - return false; - - // 34 bytes - MafiaNet::BitStream bitStream; - WriteOutOfBandHeader(&bitStream); - - if (dataLength>0) - { - bitStream.Write(data, dataLength); - } - - unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - - /* - SystemAddress systemAddress; - systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); - systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); - - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); - - SocketLayer::SendTo( socketList[realIndex], (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - */ - - RNS2_SendParameters bsp; - bsp.data = (char*) bitStream.GetData(); - bsp.length = bitStream.GetNumberOfBytesUsed(); - bsp.systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); - bsp.systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*)bsp.data, BYTES_TO_BITS(bsp.length), bsp.systemAddress); - socketList[realIndex]->Send(&bsp, _FILE_AND_LINE_); - - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakNetStatistics * RakPeer::GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns ) -{ - static RakNetStatistics staticStatistics; - RakNetStatistics *systemStats; - if (rns==0) - systemStats=&staticStatistics; - else - systemStats=rns; - - if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) - { - bool firstWrite=false; - // Return a crude sum - for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].isActive) - { - RakNetStatistics rnsTemp; - remoteSystemList[ i ].reliabilityLayer.GetStatistics(&rnsTemp); - - if (firstWrite==false) - { - memcpy(systemStats, &rnsTemp, sizeof(RakNetStatistics)); - firstWrite=true; - } - else - (*systemStats)+=rnsTemp; - } - } - return systemStats; - } - else - { - RemoteSystemStruct * rss; - rss = GetRemoteSystemFromSystemAddress( systemAddress, false, false ); - if ( rss && endThreads==false ) - { - rss->reliabilityLayer.GetStatistics(systemStats); - return systemStats; - } - } - - return 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetStatisticsList(DataStructures::List &addresses, DataStructures::List &guids, DataStructures::List &statistics) -{ - addresses.Clear(false, _FILE_AND_LINE_); - guids.Clear(false, _FILE_AND_LINE_); - statistics.Clear(false, _FILE_AND_LINE_); - - if ( remoteSystemList == 0 || endThreads == true ) - return; - - unsigned int i; - for (i=0; i < activeSystemListSize; i++) - { - if ((activeSystemList[i])->isActive && - (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED) - { - addresses.Push((activeSystemList[i])->systemAddress, _FILE_AND_LINE_ ); - guids.Push((activeSystemList[i])->guid, _FILE_AND_LINE_ ); - RakNetStatistics rns; - (activeSystemList[i])->reliabilityLayer.GetStatistics(&rns); - statistics.Push(rns, _FILE_AND_LINE_); - } - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::GetStatistics( const unsigned int index, RakNetStatistics *rns ) -{ - if (index < maximumNumberOfPeers && remoteSystemList[ index ].isActive) - { - remoteSystemList[ index ].reliabilityLayer.GetStatistics(rns); - return true; - } - return false; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetReceiveBufferSize(void) -{ - unsigned int size; - packetReturnMutex.Lock(); - size=packetReturnQueue.Size(); - packetReturnMutex.Unlock(); - return size; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread ) const -{ - unsigned i; - - if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) - return -1; - - if (systemAddress.systemIndex!=(SystemIndex)-1 && systemAddress.systemIndex < maximumNumberOfPeers && remoteSystemList[systemAddress.systemIndex].systemAddress==systemAddress && remoteSystemList[ systemAddress.systemIndex ].isActive) - return systemAddress.systemIndex; - - if (calledFromNetworkThread) - { - return GetRemoteSystemIndex(systemAddress); - } - else - { - // remoteSystemList in user and network thread - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].systemAddress == systemAddress ) - return i; - - // If no active results found, try previously active results. - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].systemAddress == systemAddress ) - return i; - } - - return -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetIndexFromGuid( const RakNetGUID guid ) -{ - unsigned i; - - if ( guid == UNASSIGNED_RAKNET_GUID ) - return -1; - - if (guid.systemIndex!=(SystemIndex)-1 && guid.systemIndex < maximumNumberOfPeers && remoteSystemList[guid.systemIndex].guid==guid && remoteSystemList[ guid.systemIndex ].isActive) - return guid.systemIndex; - - // remoteSystemList in user and network thread - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].guid == guid ) - return i; - - // If no active results found, try previously active results. - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].guid == guid ) - return i; - - return -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -#if LIBCAT_SECURITY==1 -bool RakPeer::GenerateConnectionRequestChallenge(RequestedConnectionStruct *rcs,PublicKey *publicKey) -{ - CAT_AUDIT_PRINTF("AUDIT: In GenerateConnectionRequestChallenge()\n"); - - rcs->client_handshake = 0; - rcs->publicKeyMode = PKM_INSECURE_CONNECTION; - - if (!publicKey) return true; - - switch (publicKey->publicKeyMode) - { - default: - case PKM_INSECURE_CONNECTION: - break; - - case PKM_ACCEPT_ANY_PUBLIC_KEY: - CAT_OBJCLR(rcs->remote_public_key); - rcs->client_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); - - rcs->publicKeyMode = PKM_ACCEPT_ANY_PUBLIC_KEY; - break; - - case PKM_USE_TWO_WAY_AUTHENTICATION: - if (publicKey->myPublicKey == 0 || publicKey->myPrivateKey == 0 || - publicKey->remoteServerPublicKey == 0) - { - return false; - } - - rcs->client_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); - memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); - - if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || - !rcs->client_handshake->SetIdentity(publicKey->myPublicKey, publicKey->myPrivateKey) || - !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) - { - CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object with identity for this RequestedConnectionStruct\n"); - MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - rcs->client_handshake=0; - return false; - } - - CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object with identity for this RequestedConnectionStruct -- pre-generated challenge\n"); - - rcs->publicKeyMode = PKM_USE_TWO_WAY_AUTHENTICATION; - break; - - case PKM_USE_KNOWN_PUBLIC_KEY: - if (publicKey->remoteServerPublicKey == 0) - return false; - - rcs->client_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); - memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); - - if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || - !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) - { - CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object for this RequestedConnectionStruct\n"); - MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - rcs->client_handshake=0; - return false; - } - - CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object for this RequestedConnectionStruct -- pre-generated challenge\n"); - - rcs->publicKeyMode = PKM_USE_KNOWN_PUBLIC_KEY; - break; - } - - return true; -} -#endif -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime ) -{ - RakAssert(passwordDataLength <= 256); - RakAssert(remotePort!=0); - SystemAddress systemAddress; - if (!systemAddress.FromStringExplicitPort(host,remotePort,socketList[connectionSocketIndex]->GetBoundAddress().GetIPVersion())) - return CANNOT_RESOLVE_DOMAIN_NAME; - - // Already connected? - if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) - return ALREADY_CONNECTED_TO_ENDPOINT; - - //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); - RequestedConnectionStruct *rcs = MafiaNet::OP_NEW(_FILE_AND_LINE_); - - rcs->systemAddress=systemAddress; - rcs->nextRequestTime= MafiaNet::GetTimeMS(); - rcs->requestsMade=0; - rcs->data=0; - rcs->socket=0; - rcs->extraData=extraData; - rcs->socketIndex=connectionSocketIndex; - rcs->actionToTake=RequestedConnectionStruct::CONNECT; - rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; - rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; - // No password means passwordData is null. memcpy's source is declared nonnull, - // so a null source is undefined behaviour even with a length of 0. - if (passwordDataLength > 0) - memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); - rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; - rcs->timeoutTime=timeoutTime; - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: In SendConnectionRequest()\n"); - if (!GenerateConnectionRequestChallenge(rcs,publicKey)) - return SECURITY_INITIALIZATION_FAILED; -#else - (void) publicKey; -#endif - - // Return false if already pending, else push on queue - unsigned int i=0; - requestedConnectionQueueMutex.Lock(); - for (; i < requestedConnectionQueue.Size(); i++) - { - if (requestedConnectionQueue[i]->systemAddress==systemAddress) - { - requestedConnectionQueueMutex.Unlock(); - // Not necessary - //MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); - return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; - } - } - requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); - requestedConnectionQueueMutex.Unlock(); - - return CONNECTION_ATTEMPT_STARTED; -} -ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime, RakNetSocket2* socket ) -{ - RakAssert(passwordDataLength <= 256); - SystemAddress systemAddress; - systemAddress.FromStringExplicitPort(host,remotePort); - - // Already connected? - if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) - return ALREADY_CONNECTED_TO_ENDPOINT; - - //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); - RequestedConnectionStruct *rcs = MafiaNet::OP_NEW(_FILE_AND_LINE_); - - rcs->systemAddress=systemAddress; - rcs->nextRequestTime= MafiaNet::GetTimeMS(); - rcs->requestsMade=0; - rcs->data=0; - rcs->socket=0; - rcs->extraData=extraData; - rcs->socketIndex=connectionSocketIndex; - rcs->actionToTake=RequestedConnectionStruct::CONNECT; - rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; - rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; - // No password means passwordData is null. memcpy's source is declared nonnull, - // so a null source is undefined behaviour even with a length of 0. - if (passwordDataLength > 0) - memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); - rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; - rcs->timeoutTime=timeoutTime; - rcs->socket=socket; - -#if LIBCAT_SECURITY==1 - if (!GenerateConnectionRequestChallenge(rcs,publicKey)) - return SECURITY_INITIALIZATION_FAILED; -#else - (void) publicKey; -#endif - - // Return false if already pending, else push on queue - unsigned int i=0; - requestedConnectionQueueMutex.Lock(); - for (; i < requestedConnectionQueue.Size(); i++) - { - if (requestedConnectionQueue[i]->systemAddress==systemAddress) - { - requestedConnectionQueueMutex.Unlock(); - // Not necessary - //MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); - return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; - } - } - requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); - requestedConnectionQueueMutex.Unlock(); - - return CONNECTION_ATTEMPT_STARTED; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ValidateRemoteSystemLookup(void) const -{ -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystem( const AddressOrGUID systemIdentifier, bool calledFromNetworkThread, bool onlyActive ) const -{ - if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) - return GetRemoteSystemFromGUID(systemIdentifier.rakNetGuid, onlyActive); - else - return GetRemoteSystemFromSystemAddress(systemIdentifier.systemAddress, calledFromNetworkThread, onlyActive); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread, bool onlyActive ) const -{ - unsigned i; - - if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) - return 0; - - if (calledFromNetworkThread) - { - unsigned int index = GetRemoteSystemIndex(systemAddress); - if (index!=(unsigned int) -1) - { - if (onlyActive==false || remoteSystemList[ index ].isActive==true ) - { - RakAssert(remoteSystemList[index].systemAddress==systemAddress); - return remoteSystemList + index; - } - } - } - else - { - int deadConnectionIndex=-1; - - // Active connections take priority. But if there are no active connections, return the first systemAddress match found - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].systemAddress == systemAddress) - { - if ( remoteSystemList[ i ].isActive ) - return remoteSystemList + i; - else if (deadConnectionIndex==-1) - deadConnectionIndex=i; - } - } - - if (deadConnectionIndex!=-1 && onlyActive==false) - return remoteSystemList + deadConnectionIndex; - } - - return 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromGUID( const RakNetGUID guid, bool onlyActive ) const -{ - if (guid==UNASSIGNED_RAKNET_GUID) - return 0; - - unsigned i; - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].guid == guid && (onlyActive==false || remoteSystemList[ i ].isActive)) - { - return remoteSystemList + i; - } - } - return 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ParseConnectionRequestPacket( RakPeer::RemoteSystemStruct *remoteSystem, const SystemAddress &systemAddress, const char *data, int byteSize ) -{ - MafiaNet::BitStream bs((unsigned char*) data,byteSize,false); - bs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID guid; - bs.Read(guid); - MafiaNet::Time incomingTimestamp; - bs.Read(incomingTimestamp); - unsigned char doSecurity; - bs.Read(doSecurity); - -#if LIBCAT_SECURITY==1 - unsigned char doClientKey; - if (_using_security) - { - // Ignore message on bad state - if (doSecurity != 1 || !remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()) - return; - - // Validate client proof of key - unsigned char proof[cat::EasyHandshake::PROOF_BYTES]; - bs.ReadAlignedBytes(proof, sizeof(proof)); - if (!remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->ValidateProof(proof, sizeof(proof))) - { - remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - - CAT_OBJCLR(remoteSystem->client_public_key); - - bs.Read(doClientKey); - - // Check if client wants to prove identity - if (doClientKey == 1) - { - // Read identity proof - unsigned char ident[cat::EasyHandshake::IDENTITY_BYTES]; - bs.ReadAlignedBytes(ident, sizeof(ident)); - - // If we are listening to these proofs, - if (_require_client_public_key) - { - // Validate client identity - if (!_server_handshake->VerifyInitiatorIdentity(remoteSystem->answer, ident, remoteSystem->client_public_key)) - { - MafiaNet::BitStream bitStream; - bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect - bitStream.Write((unsigned char)2); // Indicate client identity is invalid - SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); - remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - } - - // Otherwise ignore the client public key - } - else - { - // If no client key was provided but it is required, - if (_require_client_public_key) - { - MafiaNet::BitStream bitStream; - bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect - bitStream.Write((unsigned char)1); // Indicate client identity is missing - SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); - remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - } - } -#endif // LIBCAT_SECURITY - - unsigned char *password = bs.GetData()+BITS_TO_BYTES(bs.GetReadOffset()); - int passwordLength = byteSize - BITS_TO_BYTES(bs.GetReadOffset()); - if ( incomingPasswordLength != passwordLength || - memcmp( password, incomingPassword, incomingPasswordLength ) != 0 ) - { - CAT_AUDIT_PRINTF("AUDIT: Invalid password\n"); - // This one we only send once since we don't care if it arrives. - MafiaNet::BitStream bitStream; - bitStream.Write((MessageID)ID_INVALID_PASSWORD); - bitStream.Write(GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - - // OK - remoteSystem->connectMode=RemoteSystemStruct::HANDLING_CONNECTION_REQUEST; - - OnConnectionRequest( remoteSystem, incomingTimestamp ); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::OnConnectionRequest( RakPeer::RemoteSystemStruct *remoteSystem, MafiaNet::Time incomingTimestamp ) -{ - MafiaNet::BitStream bitStream; - bitStream.Write((MessageID)ID_CONNECTION_REQUEST_ACCEPTED); - bitStream.Write(remoteSystem->systemAddress); - SystemIndex systemIndex = (SystemIndex) GetIndexFromSystemAddress( remoteSystem->systemAddress, true ); - RakAssert(systemIndex!=65535); - bitStream.Write(systemIndex); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - bitStream.Write(ipList[i]); - bitStream.Write(incomingTimestamp); - bitStream.Write(MafiaNet::GetTime()); - - SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, remoteSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::NotifyAndFlagForShutdown( const SystemAddress systemAddress, bool performImmediate, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority, const MafiaNet::BitStream *reasonData ) -{ - MafiaNet::BitStream temp( sizeof(unsigned char) ); - temp.Write( (MessageID)ID_DISCONNECTION_NOTIFICATION ); - // Optionally append a caller-supplied reason payload right after the 1-byte ID. The ID occupies exactly 8 bits so - // the payload stays byte-aligned; the remote reads it from packet->data+1 (length packet->length-1). Wire-backward - // compatible: peers that only inspect data[0] ignore the extra bytes. - if (reasonData != nullptr && reasonData->GetNumberOfBytesUsed() > 0) - temp.Write( (const char*)reasonData->GetData(), reasonData->GetNumberOfBytesUsed() ); - if (performImmediate) - { - SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, MafiaNet::Reliability::ReliableOrdered, orderingChannel, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); - RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(systemAddress, true, true); - rss->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; - } - else - { - SendBuffered((const char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, MafiaNet::Reliability::ReliableOrdered, orderingChannel, systemAddress, false, RemoteSystemStruct::DISCONNECT_ASAP, 0); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearDisconnectReason( RemoteSystemStruct *remoteSystem ) -{ - if (remoteSystem==0) - return; - if (remoteSystem->disconnectReasonData!=0) - { - rakFree_Ex(remoteSystem->disconnectReasonData, _FILE_AND_LINE_); - remoteSystem->disconnectReasonData=0; - } - remoteSystem->disconnectReasonLength=0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearSessionConfig( RemoteSystemStruct *remoteSystem ) -{ - if (remoteSystem==0) - return; - if (remoteSystem->remoteSessionConfigData!=0) - { - rakFree_Ex(remoteSystem->remoteSessionConfigData, _FILE_AND_LINE_); - remoteSystem->remoteSessionConfigData=0; - } - remoteSystem->remoteSessionConfigLength=0; - if (remoteSystem->withheldConnectionPacketData!=0) - { - rakFree_Ex(remoteSystem->withheldConnectionPacketData, _FILE_AND_LINE_); - remoteSystem->withheldConnectionPacketData=0; - } - remoteSystem->withheldConnectionPacketLength=0; - remoteSystem->sessionConfigAwaitingLocalDecision=false; - remoteSystem->sessionConfigIsConnectingSide=false; - remoteSystem->connectionReportedToApplication=false; - remoteSystem->sessionConfigStartTime=0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::StoreRemoteSessionConfig( RemoteSystemStruct *remoteSystem, const unsigned char *data, unsigned int length ) -{ - if (remoteSystem==0) - return false; - - // An oversized payload is a protocol violation, not something to truncate: the sender enforces the - // same cap in SetSessionConfig, so anything larger did not come from a well-behaved peer. - if (length > MAXIMUM_SESSION_CONFIG_SIZE) - return false; - - if (remoteSystem->remoteSessionConfigData!=0) - { - rakFree_Ex(remoteSystem->remoteSessionConfigData, _FILE_AND_LINE_); - remoteSystem->remoteSessionConfigData=0; - } - remoteSystem->remoteSessionConfigLength=0; - - if (data==0 || length==0) - return true; - - // One extra byte, always zeroed and never counted in the reported length. The payload is - // attacker-controlled and arbitrary bytes, so an application that reaches for strlen/printf on it - // would otherwise run off the end of the buffer. Length remains the authoritative bound. - remoteSystem->remoteSessionConfigData = (unsigned char*) rakMalloc_Ex(length + 1, _FILE_AND_LINE_); - if (remoteSystem->remoteSessionConfigData==0) - return false; - - memcpy(remoteSystem->remoteSessionConfigData, data, length); - remoteSystem->remoteSessionConfigData[length] = 0; - remoteSystem->remoteSessionConfigLength = length; - return true; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Replay the connection packet that was held back while the session handshake ran. This is the single -// point at which an application learns a connection exists, and by construction it runs only once the -// remote's session payload is already stored on the slot. -void RakPeer::ProduceWithheldConnectionPacket( RemoteSystemStruct *remoteSystem, MessageID messageId ) -{ - if (remoteSystem==0) - return; - - Packet *packet; - if (remoteSystem->withheldConnectionPacketData!=0 && remoteSystem->withheldConnectionPacketLength>0) - { - packet=AllocPacket(remoteSystem->withheldConnectionPacketLength, _FILE_AND_LINE_); - memcpy(packet->data, remoteSystem->withheldConnectionPacketData, remoteSystem->withheldConnectionPacketLength); - packet->bitSize = remoteSystem->withheldConnectionPacketLength * 8; - rakFree_Ex(remoteSystem->withheldConnectionPacketData, _FILE_AND_LINE_); - remoteSystem->withheldConnectionPacketData=0; - remoteSystem->withheldConnectionPacketLength=0; - } - else - { - // The original bytes could not be stashed (allocation failure); the message id alone still tells - // the application a connection exists, which is what every caller keys off. - packet=AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); - packet->data[0]=messageId; - packet->bitSize = sizeof(MessageID) * 8; - } - - packet->systemAddress = remoteSystem->systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex = packet->systemAddress.systemIndex; - remoteSystem->connectionReportedToApplication=true; - AddPacketToProducer(packet); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ProduceSessionAbandoned( RemoteSystemStruct *remoteSystem ) -{ - if (remoteSystem==0 || remoteSystem->weInitiatedTheConnection || remoteSystem->sessionConfigAwaitingLocalDecision==false) - return; - - // Once only: the flag is what made the request the application's to answer. - remoteSystem->sessionConfigAwaitingLocalDecision=false; - - Packet *packet=AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); - packet->data[0]=(unsigned char)ID_SESSION_CONFIG_ABANDONED; - packet->bitSize=sizeof(MessageID)*8; - packet->systemAddress=remoteSystem->systemAddress; - packet->systemAddress.systemIndex=remoteSystem->remoteSystemIndex; - packet->guid=remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Server side of the session handshake: answer the client's request, promote the connection to -// CONNECTED and release the ID_NEW_INCOMING_CONNECTION that was withheld. -void RakPeer::SendSessionConfigResponse( RemoteSystemStruct *remoteSystem, const char *data, unsigned int length ) -{ - if (remoteSystem==0) - return; - - MafiaNet::BitStream bitStream; - bitStream.Write((MessageID)ID_SESSION_CONFIG); - if (data && length > 0) - bitStream.Write(data, length); - SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, remoteSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); - - remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; - remoteSystem->sessionConfigStartTime=0; - ProduceWithheldConnectionPacket(remoteSystem, (MessageID)ID_NEW_INCOMING_CONNECTION); - - PingInternal( remoteSystem->systemAddress, true, MafiaNet::Reliability::Unreliable ); - quitAndDataEvents.SetEvent(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendSessionConfigRejection( RemoteSystemStruct *remoteSystem, const char *reason, unsigned int reasonLength ) -{ - if (remoteSystem==0) - return; - - MafiaNet::BitStream bitStream; - bitStream.Write((MessageID)ID_SESSION_CONFIG_REJECTED); - if (reason && reasonLength > 0) - bitStream.Write(reason, reasonLength); - SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, remoteSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); - - // The refusal has to reach the client before the socket goes away, so drop on no-ack rather than - // silently: the client turns ID_SESSION_CONFIG_REJECTED into ID_CONNECTION_ATTEMPT_FAILED itself. - remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ON_NO_ACK; - remoteSystem->sessionConfigStartTime = 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetNumberOfRemoteInitiatedConnections( void ) const -{ - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - unsigned int numberOfIncomingConnections; - numberOfIncomingConnections = 0; - unsigned int i; - for (i=0; i < activeSystemListSize; i++) - { - if ((activeSystemList[i])->isActive && - (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED && - (activeSystemList[i])->weInitiatedTheConnection==false - ) - { - numberOfIncomingConnections++; - } - } - return numberOfIncomingConnections; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct * RakPeer::AssignSystemAddressToRemoteSystemList( const SystemAddress systemAddress, RemoteSystemStruct::ConnectMode connectionMode, RakNetSocket2* incomingRakNetSocket, bool *thisIPConnectedRecently, SystemAddress bindingAddress, int incomingMTU, RakNetGUID guid, bool useSecurity ) -{ - RemoteSystemStruct * remoteSystem; - unsigned i,j,assignedIndex; - MafiaNet::TimeMS time = MafiaNet::GetTimeMS(); -#ifdef _DEBUG - RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); -#endif - - if (limitConnectionFrequencyFromTheSameIP) - { - if (IsLoopbackAddress(systemAddress,false)==false) - { - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if ( remoteSystemList[ i ].isActive==true && - remoteSystemList[ i ].systemAddress.EqualsExcludingPort(systemAddress) && - time >= remoteSystemList[ i ].connectionTime && - time - remoteSystemList[ i ].connectionTime < 100 - ) - { - // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots - // Ignore connection attempts if this IP address connected within the last 100 milliseconds - *thisIPConnectedRecently=true; - ValidateRemoteSystemLookup(); - return 0; - } - } - } - } - - // Don't use a different port than what we received on - bindingAddress.CopyPort(incomingRakNetSocket->GetBoundAddress()); - - *thisIPConnectedRecently=false; - for ( assignedIndex = 0; assignedIndex < maximumNumberOfPeers; assignedIndex++ ) - { - if ( remoteSystemList[ assignedIndex ].isActive==false ) - { - // printf("--- Address %s has become active\n", systemAddress.ToString()); - - remoteSystem=remoteSystemList+assignedIndex; - ReferenceRemoteSystem(systemAddress, assignedIndex); - // Stale reason payload from a prior occupant of this slot must never leak into a new connection. - ClearDisconnectReason(remoteSystem); - // Same for session state: peer.h documents it as cleared on slot reuse, and only the teardown - // paths do it today. Without this, any future path that deactivates a slot without going - // through CloseConnectionInternal2 would hand the previous occupant payload to the new one. - ClearSessionConfig(remoteSystem); - remoteSystem->MTUSize=defaultMTUSize; - remoteSystem->guid=guid; - remoteSystem->isActive = true; // This one line causes future incoming packets to go through the reliability layer - // Reserve this reliability layer for ourselves. - // - // incomingMTU comes off the wire -- ID_OPEN_CONNECTION_REQUEST_2 on the accepting side, - // ID_OPEN_CONNECTION_REPLY_2 on the connecting one -- so it is whatever the remote peer - // said, and must be clamped rather than trusted. MTUSize sizes every datagram the - // reliability layer builds into buffers declared MAXIMUM_MTU_SIZE bytes long, so a - // larger value writes past the end of them. A peer built against a higher cap is enough - // to produce one; a hostile peer can name any uint16. This used to be a bare RakAssert, - // which is compiled out of exactly the builds that ship. - if (incomingMTU > MAXIMUM_MTU_SIZE) - incomingMTU = MAXIMUM_MTU_SIZE; - if (incomingMTU > remoteSystem->MTUSize) - remoteSystem->MTUSize=incomingMTU; - RakAssert(remoteSystem->MTUSize <= MAXIMUM_MTU_SIZE); - remoteSystem->reliabilityLayer.Reset(true, remoteSystem->MTUSize, useSecurity); - remoteSystem->reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); - remoteSystem->reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); - remoteSystem->reliabilityLayer.SetTimeoutTime(defaultTimeoutTime); - AddToActiveSystemList(assignedIndex); - if (incomingRakNetSocket->GetBoundAddress()==bindingAddress) - { - remoteSystem->rakNetSocket=incomingRakNetSocket; - } - else - { - // Originally this searched ipList for the internal address the datagram came in on and - // forced the reply onto it, so a multi-homed machine answers from the same IP. That was - // disabled upstream (06/26/09, unconfirmed report that the Vista firewall blocks the reply - // if we force a binding), which left the search computing an index nobody read. Always - // reply on the socket the datagram came in on. - remoteSystem->rakNetSocket=incomingRakNetSocket; - } - - for ( j = 0; j < (unsigned) PING_TIMES_ARRAY_SIZE; j++ ) - { - remoteSystem->pingAndClockDifferential[ j ].pingTime = 65535; - remoteSystem->pingAndClockDifferential[ j ].clockDifferential = 0; - } - - remoteSystem->connectMode=connectionMode; - remoteSystem->pingAndClockDifferentialWriteIndex = 0; - remoteSystem->lowestPing = 65535; - remoteSystem->nextPingTime = 0; // Ping immediately - remoteSystem->weInitiatedTheConnection = false; - remoteSystem->connectionTime = time; - remoteSystem->myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; - remoteSystem->lastReliableSend=time; - -#ifdef _DEBUG - int indexLoopupCheck=GetIndexFromSystemAddress( systemAddress, true ); - if ((int) indexLoopupCheck!=(int) assignedIndex) - { - RakAssert((int) indexLoopupCheck==(int) assignedIndex); - } -#endif - - return remoteSystem; - } - } - - return 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Adjust the first four bytes (treated as unsigned int) of the pointer -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ShiftIncomingTimestamp( unsigned char *data, const SystemAddress &systemAddress ) const -{ -#ifdef _DEBUG - RakAssert( IsActive() ); - RakAssert( data ); -#endif - - MafiaNet::BitStream timeBS( data, sizeof(MafiaNet::Time), false); - MafiaNet::Time encodedTimestamp; - timeBS.Read(encodedTimestamp); - - encodedTimestamp = encodedTimestamp - GetBestClockDifferential( systemAddress ); - timeBS.SetWriteOffset(0); - timeBS.Write(encodedTimestamp); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm -MafiaNet::Time RakPeer::GetBestClockDifferential( const SystemAddress systemAddress ) const -{ - RemoteSystemStruct *remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - - if ( remoteSystem == 0 ) - return 0; - - return GetClockDifferentialInt(remoteSystem); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::RemoteSystemLookupHashIndex(const SystemAddress &sa) const -{ - return SystemAddress::ToInteger(sa) % ((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ReferenceRemoteSystem(const SystemAddress &sa, unsigned int remoteSystemListIndex) -{ -// #ifdef _DEBUG -// for ( unsigned int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) -// { -// if (remoteSystemList[remoteSystemIndex].isActive ) -// { -// unsigned int debugHashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); -// RakAssert(debugHashIndex==remoteSystemIndex); -// } -// } -// #endif - - - SystemAddress oldAddress = remoteSystemList[remoteSystemListIndex].systemAddress; - if (oldAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - // The system might be active if rerouting -// RakAssert(remoteSystemList[remoteSystemListIndex].isActive==false); - - // Remove the reference if the reference is pointing to this inactive system - if (GetRemoteSystem(oldAddress)==&remoteSystemList[remoteSystemListIndex]) - DereferenceRemoteSystem(oldAddress); - } - DereferenceRemoteSystem(sa); - -// #ifdef _DEBUG -// for ( unsigned int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) -// { -// if (remoteSystemList[remoteSystemIndex].isActive ) -// { -// unsigned int debugHashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); -// if (debugHashIndex!=remoteSystemIndex) -// { -// RakAssert(debugHashIndex==remoteSystemIndex); -// } -// } -// } -// #endif - - - remoteSystemList[remoteSystemListIndex].systemAddress=sa; - - unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); - RemoteSystemIndex *rsi; - rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); - if (remoteSystemLookup[hashIndex]==0) - { - rsi->next=0; - rsi->index=remoteSystemListIndex; - remoteSystemLookup[hashIndex]=rsi; - } - else - { - RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; - while (cur->next!=0) - { - cur=cur->next; - } - - rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); - rsi->next=0; - rsi->index=remoteSystemListIndex; - cur->next=rsi; - } - -// #ifdef _DEBUG -// for ( unsigned int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) -// { -// if (remoteSystemList[remoteSystemIndex].isActive ) -// { -// unsigned int debugHashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); -// RakAssert(debugHashIndex==remoteSystemIndex); -// } -// } -// #endif - - - RakAssert(GetRemoteSystemIndex(sa)==remoteSystemListIndex); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DereferenceRemoteSystem(const SystemAddress &sa) -{ - unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); - RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; - RemoteSystemIndex *last = 0; - while (cur!=0) - { - if (remoteSystemList[cur->index].systemAddress==sa) - { - if (last==0) - { - remoteSystemLookup[hashIndex]=cur->next; - } - else - { - last->next=cur->next; - } - remoteSystemIndexPool.Release(cur,_FILE_AND_LINE_); - break; - } - last=cur; - cur=cur->next; - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetRemoteSystemIndex(const SystemAddress &sa) const -{ - unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); - RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; - while (cur!=0) - { - if (remoteSystemList[cur->index].systemAddress==sa) - return cur->index; - cur=cur->next; - } - return (unsigned int) -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct* RakPeer::GetRemoteSystem(const SystemAddress &sa) const -{ - unsigned int remoteSystemIndex = GetRemoteSystemIndex(sa); - if (remoteSystemIndex==(unsigned int)-1) - return 0; - return remoteSystemList + remoteSystemIndex; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearRemoteSystemLookup(void) -{ - remoteSystemIndexPool.Clear(_FILE_AND_LINE_); - MafiaNet::OP_DELETE_ARRAY(remoteSystemLookup,_FILE_AND_LINE_); - remoteSystemLookup=0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AddToActiveSystemList(unsigned int remoteSystemListIndex) -{ - activeSystemList[activeSystemListSize++]=remoteSystemList+remoteSystemListIndex; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RemoveFromActiveSystemList(const SystemAddress &sa) -{ - unsigned int i; - for (i=0; i < activeSystemListSize; i++) - { - RemoteSystemStruct *rss=activeSystemList[i]; - if (rss->systemAddress==sa) - { - activeSystemList[i]=activeSystemList[activeSystemListSize-1]; - activeSystemListSize--; - return; - } - } - RakAssert("activeSystemList invalid, entry not found in RemoveFromActiveSystemList. Ensure that AddToActiveSystemList and RemoveFromActiveSystemList are called by the same thread." && 0); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -/* -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::LookupIndexUsingHashIndex(const SystemAddress &sa) const -{ - unsigned int scanCount=0; - unsigned int index = RemoteSystemLookupHashIndex(sa); - if (remoteSystemLookup[index].index==(unsigned int)-1) - return (unsigned int) -1; - while (remoteSystemList[remoteSystemLookup[index].index].systemAddress!=sa) - { - if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) - index=0; - if (++scanCount>(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) - return (unsigned int) -1; - if (remoteSystemLookup[index].index==-1) - return (unsigned int) -1; - } - return index; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::RemoteSystemListIndexUsingHashIndex(const SystemAddress &sa) const -{ - unsigned int index = LookupIndexUsingHashIndex(sa); - if (index!=(unsigned int) -1) - { - return remoteSystemLookup[index].index; - } - return (unsigned int) -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::FirstFreeRemoteSystemLookupIndex(const SystemAddress &sa) const -{ -// unsigned int collisionCount=0; - unsigned int index = RemoteSystemLookupHashIndex(sa); - while (remoteSystemLookup[index].index!=(unsigned int)-1) - { - if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) - index=0; -// collisionCount++; - } -// printf("%i collisions. Using index %i\n", collisionCount, index); - return index; -} -*/ -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsLoopbackAddress(const AddressOrGUID &systemIdentifier, bool matchPort) const -{ - if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) - return systemIdentifier.rakNetGuid==myGuid; - - for (int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS && ipList[i]!=UNASSIGNED_SYSTEM_ADDRESS; i++) - { - if (matchPort) - { - if (ipList[i]==systemIdentifier.systemAddress) - return true; - } - else - { - if (ipList[i].EqualsExcludingPort(systemIdentifier.systemAddress)) - return true; - } - } - - return (matchPort==true && systemIdentifier.systemAddress==firstExternalID) || - (matchPort==false && systemIdentifier.systemAddress.EqualsExcludingPort(firstExternalID)); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetLoopbackAddress(void) const -{ - - return ipList[0]; - - - -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::AllowIncomingConnections(void) const -{ - // Counts peers still running the session handshake as well as fully established ones. - // GetNumberOfRemoteInitiatedConnections() deliberately reports only CONNECTED peers -- that is what - // an application means by "players" -- but admission control must not share that view: a peer parked - // in EXCHANGING_SESSION_DATA already owns a slot, and counting only CONNECTED would let peers that - // stall the handshake push the real total past SetMaximumIncomingConnections(). - if ( remoteSystemList == 0 || endThreads == true ) - return false; - - return AllowIncomingConnection(UNASSIGNED_SYSTEM_ADDRESS); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::AllowIncomingConnection(const SystemAddress &address) const -{ - if ( remoteSystemList == 0 || endThreads == true ) - return false; - - SessionAdmission::Limits limits; - limits.maxIncoming = GetMaximumIncomingConnections(); - limits.maxPending = maximumPendingSessions; - limits.maxPendingPerAddress = maximumPendingSessionsPerAddress; - - SessionAdmission::Counts counts; - CountRemoteInitiatedPeers(address, counts.connected, counts.exchanging, counts.pending, counts.pendingFromAddress); - return SessionAdmission::MayStartConnecting(limits, counts); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// A pending peer is one that asked to connect and is not CONNECTED yet: still proving the transport -// (UNVERIFIED_SENDER, HANDLING_CONNECTION_REQUEST) or in the session handshake. A peer being torn down is -// in none of these, so a refusal frees its share of the pool at once rather than after the ack. -void RakPeer::CountRemoteInitiatedPeers(const SystemAddress &address, unsigned int &connected, unsigned int &exchanging, unsigned int &pending, unsigned int &pendingFromAddress) const -{ - connected = exchanging = pending = pendingFromAddress = 0; - for (unsigned int i=0; i < activeSystemListSize; i++) - { - const RemoteSystemStruct *system = activeSystemList[i]; - if (!system->isActive || system->weInitiatedTheConnection) - continue; - - switch (system->connectMode) - { - case RemoteSystemStruct::CONNECTED: - connected++; - break; - case RemoteSystemStruct::EXCHANGING_SESSION_DATA: - exchanging++; - // A peer in the handshake is pending too. - [[fallthrough]]; - case RemoteSystemStruct::UNVERIFIED_SENDER: - case RemoteSystemStruct::HANDLING_CONNECTION_REQUEST: - pending++; - if (address!=UNASSIGNED_SYSTEM_ADDRESS && system->systemAddress.EqualsExcludingPort(address)) - pendingFromAddress++; - break; - default: - break; - } - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -MafiaNet::TimeMS RakPeer::GetSessionTimeoutFor(RemoteSystemStruct *remoteSystem) const -{ - return sessionTimeout != 0 ? sessionTimeout : remoteSystem->reliabilityLayer.GetTimeoutTime(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DeallocRNS2RecvStruct(RNS2RecvStruct *s, const char *file, unsigned int line) -{ - bufferedPacketsFreePoolMutex.Lock(); - bufferedPacketsFreePool.Push(s, file, line); - bufferedPacketsFreePoolMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RNS2RecvStruct *RakPeer::AllocRNS2RecvStruct(const char *file, unsigned int line) -{ - bufferedPacketsFreePoolMutex.Lock(); - if (bufferedPacketsFreePool.Size()>0) - { - RNS2RecvStruct *s = bufferedPacketsFreePool.Pop(); - bufferedPacketsFreePoolMutex.Unlock(); - return s; - } - else - { - bufferedPacketsFreePoolMutex.Unlock(); - return MafiaNet::OP_NEW(file,line); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearBufferedPackets(void) -{ - bufferedPacketsFreePoolMutex.Lock(); - while (bufferedPacketsFreePool.Size()>0) - MafiaNet::OP_DELETE(bufferedPacketsFreePool.Pop(), _FILE_AND_LINE_); - bufferedPacketsFreePoolMutex.Unlock(); - - bufferedPacketsQueueMutex.Lock(); - while (bufferedPacketsQueue.Size()>0) - MafiaNet::OP_DELETE(bufferedPacketsQueue.Pop(), _FILE_AND_LINE_); - bufferedPacketsQueueMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetupBufferedPackets(void) -{ -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::PushBufferedPacket(RNS2RecvStruct * p) -{ - bufferedPacketsQueueMutex.Lock(); - bufferedPacketsQueue.Push(p, _FILE_AND_LINE_); - bufferedPacketsQueueMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RNS2RecvStruct *RakPeer::PopBufferedPacket(void) -{ - bufferedPacketsQueueMutex.Lock(); - if (bufferedPacketsQueue.Size()>0) - { - RNS2RecvStruct *s = bufferedPacketsQueue.Pop(); - bufferedPacketsQueueMutex.Unlock(); - return s; - } - bufferedPacketsQueueMutex.Unlock(); - return 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::PingInternal( const SystemAddress target, bool performImmediate, MafiaNet::Reliability reliability ) -{ - if ( IsActive() == false ) - return ; - - MafiaNet::BitStream bitStream(sizeof(unsigned char)+sizeof(MafiaNet::Time)); - bitStream.Write((MessageID)ID_CONNECTED_PING); - bitStream.Write(MafiaNet::GetTime()); - if (performImmediate) - SendImmediate( (char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, reliability, 0, target, false, false, MafiaNet::GetTimeUS(), 0 ); - else - Send( &bitStream, MafiaNet::Priority::Immediate, reliability, 0, target, false ); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::CloseConnectionInternal( const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority ) -{ - RakAssert(orderingChannel < 32); - - if (systemIdentifier.IsUndefined()) - return; - - if (remoteSystemList == 0 || endThreads == true) - return; - - // #med - this is flawed and needs to be resolved properly - socketList[0] is not correct here - CloseConnectionInternal2(systemIdentifier, sendDisconnectionNotification, performImmediate, orderingChannel, disconnectionNotificationPriority, *(socketList[0])); -} - -// #med - better integrate directly in CloseConnectionInternal2() -void RakPeer::CloseConnectionInternal2(const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority, RakNetSocket2& socket, const MafiaNet::BitStream *reasonData) -{ - RakAssert(orderingChannel < 32); - - if (systemIdentifier.IsUndefined()) - return; - - if ( remoteSystemList == 0 || endThreads == true ) - return; - - SystemAddress target; - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - target=systemIdentifier.systemAddress; - } - else - { - target=GetSystemAddressFromGuid(systemIdentifier.rakNetGuid); - } - - if (target != UNASSIGNED_SYSTEM_ADDRESS && performImmediate) { - target.FixForIPVersion(socket.GetBoundAddress()); - } - - if (sendDisconnectionNotification) - { - NotifyAndFlagForShutdown(target, performImmediate, orderingChannel, disconnectionNotificationPriority, reasonData); - } - else - { - if (performImmediate) - { - unsigned int index = GetRemoteSystemIndex(target); - if (index!=(unsigned int) -1) - { - if ( remoteSystemList[index].isActive ) - { - RemoveFromActiveSystemList(target); - - // Found the index to stop - // printf("--- Address %s has become inactive\n", remoteSystemList[index].systemAddress.ToString()); - remoteSystemList[index].isActive = false; - - remoteSystemList[index].guid=UNASSIGNED_RAKNET_GUID; - - // Reserve this reliability layer for ourselves - //remoteSystemList[ remoteSystemLookup[index].index ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; - - // Clear any remaining messages - RakAssert(remoteSystemList[index].MTUSize <= MAXIMUM_MTU_SIZE); - remoteSystemList[index].reliabilityLayer.Reset(false, remoteSystemList[index].MTUSize, false); - - // Free any stashed disconnect-reason payload (e.g. delivered already, or never consumed) - ClearDisconnectReason(&remoteSystemList[index]); - - // Free any session-handshake state left over from an exchange that never completed - ClearSessionConfig(&remoteSystemList[index]); - - // Not using this socket - remoteSystemList[index].rakNetSocket = 0; - } - } - } - else - { - BufferedCommandStruct *bcs; - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->command=BufferedCommandStruct::BCS_CLOSE_CONNECTION; - bcs->systemIdentifier=target; - // #med - review whether this is safe here - can't the socket be closed/released before its used? - bcs->socket = &socket; - bcs->data=0; - bcs->orderingChannel=orderingChannel; - bcs->priority=disconnectionNotificationPriority; - bufferedCommands.Push(bcs); - } - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendBuffered( const char *data, BitSize_t numberOfBitsToSend, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) -{ - BufferedCommandStruct *bcs; - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->data = (char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES(numberOfBitsToSend), _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy - if (bcs->data==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); - return; - } - - RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); - RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); - RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); - - memcpy(bcs->data, data, (size_t) BITS_TO_BYTES(numberOfBitsToSend)); - bcs->numberOfBitsToSend=numberOfBitsToSend; - bcs->priority=priority; - bcs->reliability=reliability; - bcs->orderingChannel=orderingChannel; - bcs->systemIdentifier=systemIdentifier; - bcs->broadcast=broadcast; - bcs->connectionMode=connectionMode; - bcs->receipt=receipt; - bcs->command=BufferedCommandStruct::BCS_SEND; - bufferedCommands.Push(bcs); - - if (priority==MafiaNet::Priority::Immediate) - { - // Forces pending sends to go out now, rather than waiting to the next update interval - quitAndDataEvents.SetEvent(); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendBufferedList( const char **data, const int *lengths, const int numParameters, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) -{ - BufferedCommandStruct *bcs; - unsigned int totalLength=0; - unsigned int lengthOffset; - int i; - for (i=0; i < numParameters; i++) - { - if (lengths[i]>0) - totalLength+=lengths[i]; - } - if (totalLength==0) - return; - - char *dataAggregate; - dataAggregate = (char*) rakMalloc_Ex( (size_t) totalLength, _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy - if (dataAggregate==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - return; - } - for (i=0, lengthOffset=0; i < numParameters; i++) - { - if (lengths[i]>0) - { - memcpy(dataAggregate+lengthOffset, data[i], lengths[i]); - lengthOffset+=lengths[i]; - } - } - - if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) - { - SendLoopback(dataAggregate,totalLength); - rakFree_Ex(dataAggregate,_FILE_AND_LINE_); - return; - } - - RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); - RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); - RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->data = dataAggregate; - bcs->numberOfBitsToSend=BYTES_TO_BITS(totalLength); - bcs->priority=priority; - bcs->reliability=reliability; - bcs->orderingChannel=orderingChannel; - bcs->systemIdentifier=systemIdentifier; - bcs->broadcast=broadcast; - bcs->connectionMode=connectionMode; - bcs->receipt=receipt; - bcs->command=BufferedCommandStruct::BCS_SEND; - bufferedCommands.Push(bcs); - - if (priority==MafiaNet::Priority::Immediate) - { - // Forces pending sends to go out now, rather than waiting to the next update interval - quitAndDataEvents.SetEvent(); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::SendImmediate( char *data, BitSize_t numberOfBitsToSend, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool useCallerDataAllocation, MafiaNet::TimeUS currentTime, uint32_t receipt ) -{ - unsigned *sendList; - unsigned sendListSize; - bool callerDataAllocationUsed; - unsigned int remoteSystemIndex, sendListIndex; // Iterates into the list of remote systems -// unsigned numberOfBytesUsed = (unsigned) BITS_TO_BYTES(numberOfBitsToSend); - callerDataAllocationUsed=false; - - sendListSize=0; - - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - remoteSystemIndex=GetIndexFromSystemAddress( systemIdentifier.systemAddress, true ); - else if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) - remoteSystemIndex=GetSystemIndexFromGuid(systemIdentifier.rakNetGuid); - else - remoteSystemIndex=(unsigned int) -1; - - // 03/06/06 - If broadcast is false, use the optimized version of GetIndexFromSystemAddress - if (broadcast==false) - { - if (remoteSystemIndex==(unsigned int) -1) - { -#ifdef _DEBUG -// int debugIndex = GetRemoteSystemIndex(systemIdentifier.systemAddress); -#endif - return false; - } - - #if USE_ALLOCA==1 - sendList=(unsigned *)alloca(sizeof(unsigned)); - #else - sendList = (unsigned *) rakMalloc_Ex(sizeof(unsigned), _FILE_AND_LINE_); - #endif - - if (remoteSystemList[remoteSystemIndex].isActive && - remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP && - remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY && - remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ON_NO_ACK) - { - sendList[0]=remoteSystemIndex; - sendListSize=1; - } - } - else - { - #if USE_ALLOCA==1 - sendList=(unsigned *)alloca(sizeof(unsigned)*maximumNumberOfPeers); - #else - sendList = (unsigned *) rakMalloc_Ex(sizeof(unsigned)*maximumNumberOfPeers, _FILE_AND_LINE_); - #endif - - // remoteSystemList in network thread - unsigned int idx; - for ( idx = 0; idx < maximumNumberOfPeers; idx++ ) - { - if (remoteSystemIndex!=(unsigned int) -1 && idx==remoteSystemIndex) - continue; - - // Peers still running the session handshake are excluded: the application has not been told they - // exist, so broadcasting to them would hand application data to a peer it never accepted -- and - // possibly to one it is about to refuse with RejectSession(). Internal handshake traffic is always - // directed, so it never reaches this branch. - if ( remoteSystemList[ idx ].isActive && remoteSystemList[ idx ].systemAddress != UNASSIGNED_SYSTEM_ADDRESS && - remoteSystemList[ idx ].connectMode != RemoteSystemStruct::EXCHANGING_SESSION_DATA ) - sendList[sendListSize++]=idx; - } - } - - if (sendListSize==0) - { - #if !defined(USE_ALLOCA) - rakFree_Ex(sendList, _FILE_AND_LINE_ ); - #endif - - return false; - } - - for (sendListIndex=0; sendListIndex < sendListSize; sendListIndex++) - { - // Send may split the packet and thus deallocate data. Don't assume data is valid if we use the callerAllocationData - bool useData = useCallerDataAllocation && callerDataAllocationUsed==false && sendListIndex+1==sendListSize; - remoteSystemList[sendList[sendListIndex]].reliabilityLayer.Send( data, numberOfBitsToSend, priority, reliability, orderingChannel, useData==false, remoteSystemList[sendList[sendListIndex]].MTUSize, currentTime, receipt ); - if (useData) - callerDataAllocationUsed=true; - - if (reliability==MafiaNet::Reliability::Reliable || - reliability==MafiaNet::Reliability::ReliableOrdered || - reliability==MafiaNet::Reliability::ReliableSequenced || - reliability==MafiaNet::Reliability::ReliableWithAckReceipt || - reliability==MafiaNet::Reliability::ReliableOrderedWithAckReceipt -// || -// reliability==RELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) - remoteSystemList[sendList[sendListIndex]].lastReliableSend=(MafiaNet::TimeMS)(currentTime/(MafiaNet::TimeUS)1000); - } - -#if !defined(USE_ALLOCA) - rakFree_Ex(sendList, _FILE_AND_LINE_ ); -#endif - - // Return value only meaningful if true was passed for useCallerDataAllocation. Means the reliability layer used that data copy, so the caller should not deallocate it - return callerDataAllocationUsed; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ResetSendReceipt(void) -{ - sendReceiptSerialMutex.Lock(); - sendReceiptSerial=1; - sendReceiptSerialMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::OnConnectedPong(MafiaNet::Time sendPingTime, MafiaNet::Time sendPongTime, RemoteSystemStruct *remoteSystem) -{ - MafiaNet::Time ping; -// MafiaNet::TimeMS lastPing; - MafiaNet::Time time = MafiaNet::GetTime(); // Update the time value to be accurate - if (time > sendPingTime) - ping = time - sendPingTime; - else - ping=0; - -// lastPing = remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime; - - remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime = ( unsigned short ) ping; - // Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm - // Divide each integer by 2, rather than the sum by 2, to prevent overflow - remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].clockDifferential = sendPongTime - ( time/2 + sendPingTime/2 ); - - if ( remoteSystem->lowestPing == (unsigned short)-1 || remoteSystem->lowestPing > (int) ping ) - remoteSystem->lowestPing = (unsigned short) ping; - - if ( ++( remoteSystem->pingAndClockDifferentialWriteIndex ) == (MafiaNet::Time) PING_TIMES_ARRAY_SIZE ) - remoteSystem->pingAndClockDifferentialWriteIndex = 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearBufferedCommands(void) -{ - BufferedCommandStruct *bcs; - - while ((bcs=bufferedCommands.Pop())!=0) - { - if (bcs->data) - rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); - - bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); - } - bufferedCommands.Clear(_FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearSocketQueryOutput(void) -{ - socketQueryOutput.Clear(_FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearRequestedConnectionList(void) -{ - DataStructures::Queue freeQueue; - requestedConnectionQueueMutex.Lock(); - while (requestedConnectionQueue.Size()) - freeQueue.Push(requestedConnectionQueue.Pop(), _FILE_AND_LINE_ ); - requestedConnectionQueueMutex.Unlock(); - unsigned i; - for (i=0; i < freeQueue.Size(); i++) - { -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: In ClearRequestedConnectionList(), Deleting freeQueue index %i client_handshake %x\n", i, freeQueue[i]->client_handshake); - MafiaNet::OP_DELETE(freeQueue[i]->client_handshake,_FILE_AND_LINE_); -#endif - MafiaNet::OP_DELETE(freeQueue[i], _FILE_AND_LINE_ ); - } -} -bool RakPeer::DispatchNetworkThreadReceive(Packet *packet) -{ - pluginListNetworkThreadMutex.Lock(); - for (unsigned int i=0; i < pluginListNetworkThread.Size(); i++) - { - const PluginReceiveResult result = pluginListNetworkThread[i]->OnReceiveNetworkThread(packet); - if (result==RR_STOP_PROCESSING_AND_DEALLOCATE) - { - pluginListNetworkThreadMutex.Unlock(); - DeallocatePacket(packet); - return true; - } - if (result==RR_STOP_PROCESSING) - { - pluginListNetworkThreadMutex.Unlock(); - return true; - } - } - pluginListNetworkThreadMutex.Unlock(); - return false; -} -inline void RakPeer::AddPacketToProducer(MafiaNet::Packet *p) -{ - packetReturnMutex.Lock(); - packetReturnQueue.Push(p,_FILE_AND_LINE_); - packetReturnMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -union Buff6AndBuff8 -{ - unsigned char buff6[6]; - uint64_t buff8; -}; -uint64_t RakPeerInterface::Get64BitUniqueRandomNumber(void) -{ - // Mac address is a poor solution because you can't have multiple connections from the same system - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#if defined(_WIN32) - uint64_t g= MafiaNet::GetTimeUS(); - - MafiaNet::TimeUS lastTime, thisTime; - int j; - // Sleep a small random time, then use the last 4 bits as a source of randomness - for (j=0; j < 8; j++) - { - lastTime = MafiaNet::GetTimeUS(); - RakSleep(1); - RakSleep(0); - thisTime = MafiaNet::GetTimeUS(); - MafiaNet::TimeUS diff = thisTime-lastTime; - unsigned int diff4Bits = (unsigned int) (diff & 15); - diff4Bits <<= 32-4; - diff4Bits >>= j*4; - ((char*)&g)[j] ^= diff4Bits; - } - // Timer jitter alone collides when peers are created back to back, and is - // constant under a test's virtual clock. Mix in the same entropy as below. - static std::atomic sequence {0}; - std::random_device entropy; - g ^= ((uint64_t)entropy() << 32) ^ (uint64_t)entropy(); - g ^= (sequence.fetch_add(1, std::memory_order_relaxed) + 1) * 0x9E3779B97F4A7C15ull; - if (g == 0 || g == (uint64_t)-1) - g = 1; - return g; - -#else - // The time alone is not unique: two peers constructed within the same microsecond -- a server - // and a client created back to back in one process -- got the same GUID, and a peer then - // resolved the other's GUID to itself, so CloseConnection() and friends silently did nothing. - // Mix in the OS entropy source and a per-process counter. - struct timeval tv; - gettimeofday(&tv, nullptr); - uint64_t g = (uint64_t)tv.tv_usec + (uint64_t)tv.tv_sec * 1000000; - - static std::atomic sequence {0}; - std::random_device entropy; - g ^= ((uint64_t)entropy() << 32) ^ (uint64_t)entropy(); - g ^= (sequence.fetch_add(1, std::memory_order_relaxed) + 1) * 0x9E3779B97F4A7C15ull; - - // Neither value is a usable GUID: 0 means "not generated" and all-ones is UNASSIGNED_RAKNET_GUID. - if (g == 0 || g == (uint64_t)-1) - g = 1; - return g; -#endif -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GenerateGUID(void) -{ - myGuid.g=Get64BitUniqueRandomNumber(); - -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// void MafiaNet::ProcessPortUnreachable( SystemAddress systemAddress, RakPeer *rakPeer ) -// { -// (void) binaryAddress; -// (void) port; -// (void) rakPeer; -// -// } -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -namespace MafiaNet { -bool ProcessOfflineNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSocket2* rakNetSocket, bool *isOfflineMessage, MafiaNet::TimeUS timeRead ) -{ - (void) timeRead; - RakPeer::RemoteSystemStruct *remoteSystem; - MafiaNet::Packet *packet; - unsigned i; - - - char str1[64]; - systemAddress.ToString(false, str1,static_cast(64)); - if (rakPeer->IsBanned( str1 )) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - MafiaNet::BitStream bs; - bs.Write((MessageID)ID_CONNECTION_BANNED); - bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - - - RNS2_SendParameters bsp; - bsp.data = (char*) bs.GetData(); - bsp.length = bs.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*) bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - -/* - unsigned i; - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*) bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket, (char*) bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - */ - - return true; - } - - - - // The reason for all this is that the reliability layer has no way to tell between offline messages that arrived late for a player that is now connected, - // and a regular encoding. So I insert OFFLINE_MESSAGE_DATA_ID into the stream, the encoding of which is essentially impossible to hit by chance - if (length <=2) - { - *isOfflineMessage=true; - } - else if ( - ((unsigned char)data[0] == ID_UNCONNECTED_PING || - (unsigned char)data[0] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS) && - (size_t) length >= sizeof(unsigned char) + sizeof(MafiaNet::Time) + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(MafiaNet::Time), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if ((unsigned char)data[0] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char) + sizeof(MafiaNet::TimeMS) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(MafiaNet::Time) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if ( - (unsigned char)data[0] == ID_OUT_OF_BAND_INTERNAL && - (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(MessageID) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if ( - ( - (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_1 || - (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_2 || - (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_1 || - (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_2 || - (unsigned char)data[0] == ID_CONNECTION_ATTEMPT_FAILED || - (unsigned char)data[0] == ID_NO_FREE_INCOMING_CONNECTIONS || - (unsigned char)data[0] == ID_CONNECTION_BANNED || - (unsigned char)data[0] == ID_ALREADY_CONNECTED || - (unsigned char)data[0] == ID_IP_RECENTLY_CONNECTED) && - (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(MessageID), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if (((unsigned char)data[0] == ID_INCOMPATIBLE_PROTOCOL_VERSION&& - (size_t) length == sizeof(MessageID)*2 + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID))) - { - *isOfflineMessage=memcmp(data+sizeof(MessageID)*2, OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else - { - *isOfflineMessage=false; - } - - if (*isOfflineMessage) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - // These are all messages from unconnected systems. Messages here can be any size, but are never processed from connected systems. - if ( ( (unsigned char) data[ 0 ] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS - || (unsigned char)(data)[0] == ID_UNCONNECTED_PING) && (size_t) length >= sizeof(unsigned char)+sizeof(MafiaNet::Time)+sizeof(OFFLINE_MESSAGE_DATA_ID) ) - { - if ( (unsigned char)(data)[0] == ID_UNCONNECTED_PING || - rakPeer->AllowIncomingConnections() ) // Open connections with players - { - MafiaNet::BitStream inBitStream( (unsigned char *) data, length, false ); - inBitStream.IgnoreBits(8); - MafiaNet::Time sendPingTime; - inBitStream.Read(sendPingTime); - inBitStream.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - RakNetGUID remoteGuid=UNASSIGNED_RAKNET_GUID; - inBitStream.Read(remoteGuid); - - MafiaNet::BitStream outBitStream; - outBitStream.Write((MessageID)ID_UNCONNECTED_PONG); // Should be named ID_UNCONNECTED_PONG eventually - outBitStream.Write(sendPingTime); - outBitStream.Write(rakPeer->myGuid); - outBitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - - rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Lock(); - // They are connected, so append offline ping data - outBitStream.Write( (char*)rakPeer->offlinePingResponse.GetData(), rakPeer->offlinePingResponse.GetNumberOfBytesUsed() ); - rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Unlock(); - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*)outBitStream.GetData(), outBitStream.GetNumberOfBytesUsed(), systemAddress); - - RNS2_SendParameters bsp; - bsp.data = (char*) outBitStream.GetData(); - bsp.length = outBitStream.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - - // SocketLayer::SendTo( rakNetSocket, (const char*)outBitStream.GetData(), (unsigned int) outBitStream.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - - packet=rakPeer->AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); - packet->data[0]=data[0]; - packet->systemAddress = systemAddress; - packet->guid=remoteGuid; - packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - rakPeer->AddPacketToProducer(packet); - } - } - // UNCONNECTED MESSAGE Pong with no data. - else if ((unsigned char) data[ 0 ] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID) && (size_t) length < sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID)+MAX_OFFLINE_DATA_LENGTH) - { - packet=rakPeer->AllocPacket((unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(MafiaNet::Time)+sizeof(MafiaNet::TimeMS)), _FILE_AND_LINE_); - MafiaNet::BitStream bsIn((unsigned char*) data, length, false); - bsIn.IgnoreBytes(sizeof(unsigned char)); - MafiaNet::Time ping; - bsIn.Read(ping); - bsIn.Read(packet->guid); - - MafiaNet::BitStream bsOut((unsigned char*) packet->data, packet->length, false); - bsOut.ResetWritePointer(); - bsOut.Write((unsigned char)ID_UNCONNECTED_PONG); - MafiaNet::TimeMS pingMS=(MafiaNet::TimeMS)ping; - bsOut.Write(pingMS); - bsOut.WriteAlignedBytes( - (const unsigned char*)data+sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID), - length-sizeof(unsigned char)-sizeof(MafiaNet::Time)-RakNetGUID::size()-sizeof(OFFLINE_MESSAGE_DATA_ID) - ); - - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - rakPeer->AddPacketToProducer(packet); - } - else if ((unsigned char) data[ 0 ] == ID_OUT_OF_BAND_INTERNAL && - (size_t) length > sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)+RakNetGUID::size() && - (size_t) length < MAX_OFFLINE_DATA_LENGTH+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)+RakNetGUID::size()) - { - unsigned int dataLength = (unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(MessageID)); - RakAssert(dataLength<1024); - packet=rakPeer->AllocPacket(dataLength+1, _FILE_AND_LINE_); - RakAssert(packet->length<1024); - - MafiaNet::BitStream bs2((unsigned char*) data, length, false); - bs2.IgnoreBytes(sizeof(MessageID)); - bs2.Read(packet->guid); - - if (data[sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size()]==ID_ADVERTISE_SYSTEM) - { - packet->length--; - packet->bitSize=BYTES_TO_BITS(packet->length); - packet->data[0]=ID_ADVERTISE_SYSTEM; - memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)*2 + RakNetGUID::size(), dataLength-1); - } - else - { - packet->data[0]=ID_OUT_OF_BAND_INTERNAL; - memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size(), dataLength); - } - - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - rakPeer->AddPacketToProducer(packet); - } - else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_1) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - MafiaNet::BitStream bsIn((unsigned char*) data,length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - bsIn.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - RakNetGUID serverGuid; - bsIn.Read(serverGuid); - unsigned char serverHasSecurity; - uint32_t cookie; - (void) cookie; - bsIn.Read(serverHasSecurity); - // Even if the server has security, it may not be required of us if we are in the security exception list - if (serverHasSecurity) - { - bsIn.Read(cookie); - } - - MafiaNet::BitStream bsOut; - bsOut.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_2); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - if (serverHasSecurity) - bsOut.Write(cookie); - - rakPeer->requestedConnectionQueueMutex.Lock(); - for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) - { - RakPeer::RequestedConnectionStruct *rcs; - rcs=rakPeer->requestedConnectionQueue[i]; - if (rcs->systemAddress==systemAddress) - { - if (serverHasSecurity) - { -#if LIBCAT_SECURITY==1 - unsigned char public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; - bsIn.ReadAlignedBytes(public_key, sizeof(public_key)); - - if (rcs->publicKeyMode==PKM_ACCEPT_ANY_PUBLIC_KEY) - { - memcpy(rcs->remote_public_key, public_key, cat::EasyHandshake::PUBLIC_KEY_BYTES); - if (!rcs->client_handshake->Initialize(public_key) || - !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) - { - CAT_AUDIT_PRINTF("AUDIT: Server passed a bad public key with PKM_ACCEPT_ANY_PUBLIC_KEY"); - rakPeer->requestedConnectionQueueMutex.Unlock(); - return true; - } - } - - if (cat::SecureEqual(public_key, - rcs->remote_public_key, - cat::EasyHandshake::PUBLIC_KEY_BYTES)==false) - { - rakPeer->requestedConnectionQueueMutex.Unlock(); - CAT_AUDIT_PRINTF("AUDIT: Expected public key does not match what was sent by server -- Reporting back ID_PUBLIC_KEY_MISMATCH to user\n"); - - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_PUBLIC_KEY_MISMATCH; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=serverGuid; - rakPeer->AddPacketToProducer(packet); - return true; - } - - if (rcs->client_handshake==0) - { - // Message does not contain a challenge - // We might still pass if we are in the security exception list - bsOut.Write((unsigned char)0); - } - else - { - // Message contains a challenge - bsOut.Write((unsigned char)1); - // challenge - CAT_AUDIT_PRINTF("AUDIT: Sending challenge\n"); - bsOut.WriteAlignedBytes((const unsigned char*) rcs->handshakeChallenge,cat::EasyHandshake::CHALLENGE_BYTES); - } -#else // LIBCAT_SECURITY - // Message does not contain a challenge - bsOut.Write((unsigned char)0); -#endif // LIBCAT_SECURITY - } - else - { - // Server does not need security -#if LIBCAT_SECURITY==1 - if (rcs->client_handshake!=0) - { - rakPeer->requestedConnectionQueueMutex.Unlock(); - CAT_AUDIT_PRINTF("AUDIT: Security disabled by server but we expected security (indicated by client_handshake not null) so failing!\n"); - - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_OUR_SYSTEM_REQUIRES_SECURITY; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=serverGuid; - rakPeer->AddPacketToProducer(packet); - return true; - } -#endif // LIBCAT_SECURITY - - } - - uint16_t mtu; - bsIn.Read(mtu); - - // Binding address - bsOut.Write(rcs->systemAddress); - rakPeer->requestedConnectionQueueMutex.Unlock(); - // MTU - bsOut.Write(mtu); - // Our guid - bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), rcs->systemAddress); - - // SocketLayer::SendTo( rakPeer->socketList[rcs->socketIndex], (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), rcs->systemAddress, _FILE_AND_LINE_ ); - - RNS2_SendParameters bsp; - bsp.data = (char*) bsOut.GetData(); - bsp.length = bsOut.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - - return true; - } - } - rakPeer->requestedConnectionQueueMutex.Unlock(); - } - else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_2) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - MafiaNet::BitStream bs((unsigned char*) data,length,false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - RakNetGUID guid; - bs.Read(guid); - SystemAddress bindingAddress; - [[maybe_unused]] bool b = bs.Read(bindingAddress); // only checked by RakAssert (debug builds) - RakAssert(b); - uint16_t mtu; - b=bs.Read(mtu); - RakAssert(b); - bool doSecurity=false; - b=bs.Read(doSecurity); - RakAssert(b); - -#if LIBCAT_SECURITY==1 - char answer[cat::EasyHandshake::ANSWER_BYTES]; - CAT_AUDIT_PRINTF("AUDIT: Got ID_OPEN_CONNECTION_REPLY_2 and given doSecurity=%i\n", (int)doSecurity); - if (doSecurity) - { - CAT_AUDIT_PRINTF("AUDIT: Reading cookie and public key\n"); - bs.ReadAlignedBytes((unsigned char*) answer, sizeof(answer)); - } - cat::ClientEasyHandshake *client_handshake=0; -#endif // LIBCAT_SECURITY - - RakPeer::RequestedConnectionStruct *rcs; - bool unlock=true; - rakPeer->requestedConnectionQueueMutex.Lock(); - for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) - { - rcs=rakPeer->requestedConnectionQueue[i]; - - - if (rcs->systemAddress==systemAddress) - { -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: System address matches an entry in the requestedConnectionQueue and doSecurity=%i\n", (int)doSecurity); - if (doSecurity) - { - if (rcs->client_handshake==0) - { - CAT_AUDIT_PRINTF("AUDIT: Server wants security but we didn't set a public key -- Reporting back ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY to user\n"); - rakPeer->requestedConnectionQueueMutex.Unlock(); - - packet=rakPeer->AllocPacket(2, _FILE_AND_LINE_); - packet->data[ 0 ] = ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY; // Attempted a connection and couldn't - packet->data[ 1 ] = 0; // Indicate server public key is missing - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=guid; - rakPeer->AddPacketToProducer(packet); - return true; - } - - CAT_AUDIT_PRINTF("AUDIT: Looks good, preparing to send challenge to server! client_handshake = %x\n", client_handshake); - } - -#endif // LIBCAT_SECURITY - - rakPeer->requestedConnectionQueueMutex.Unlock(); - unlock=false; - - RakAssert(rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT); - // You might get this when already connected because of cross-connections - bool thisIPConnectedRecently=false; - remoteSystem=rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - if (remoteSystem==0) - { - if (rcs->socket == 0) - { - remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); - } - else - { - remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rcs->socket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); - } - } - - // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots - // Ignore connection attempts if this IP address connected within the last 100 milliseconds - if (thisIPConnectedRecently==false) - { - // Don't check GetRemoteSystemFromGUID, server will verify - if (remoteSystem) - { - // Move pointer from RequestedConnectionStruct to RemoteSystemStruct -#if LIBCAT_SECURITY==1 - cat::u8 ident[cat::EasyHandshake::IDENTITY_BYTES]; - bool doIdentity = false; - - if (rcs->client_handshake) - { - CAT_AUDIT_PRINTF("AUDIT: Processing answer\n"); - if (rcs->publicKeyMode == PKM_USE_TWO_WAY_AUTHENTICATION) - { - if (!rcs->client_handshake->ProcessAnswerWithIdentity(answer, ident, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) - { - CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); - return true; - } - - doIdentity = true; - } - else - { - if (!rcs->client_handshake->ProcessAnswer(answer, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) - { - CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); - return true; - } - } - CAT_AUDIT_PRINTF("AUDIT: Success!\n"); - - MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - rcs->client_handshake=0; - } -#endif // LIBCAT_SECURITY - - remoteSystem->weInitiatedTheConnection=true; - remoteSystem->connectMode=RakPeer::RemoteSystemStruct::REQUESTED_CONNECTION; - if (rcs->timeoutTime!=0) - remoteSystem->reliabilityLayer.SetTimeoutTime(rcs->timeoutTime); - - MafiaNet::BitStream temp; - temp.Write( (MessageID)ID_CONNECTION_REQUEST); - temp.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - temp.Write(MafiaNet::GetTime()); - -#if LIBCAT_SECURITY==1 - temp.Write((unsigned char)(doSecurity ? 1 : 0)); - - if (doSecurity) - { - unsigned char proof[32]; - remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->GenerateProof(proof, sizeof(proof)); - temp.WriteAlignedBytes(proof, sizeof(proof)); - - temp.Write((unsigned char)(doIdentity ? 1 : 0)); - - if (doIdentity) - { - temp.WriteAlignedBytes(ident, sizeof(ident)); - } - } -#else - temp.Write((unsigned char)0); -#endif // LIBCAT_SECURITY - - if ( rcs->outgoingPasswordLength > 0 ) - temp.Write( ( char* ) rcs->outgoingPassword, rcs->outgoingPasswordLength ); - - rakPeer->SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, timeRead, 0 ); - } - else - { - // Failed, no connections available anymore - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=guid; - rakPeer->AddPacketToProducer(packet); - } - } - - rakPeer->requestedConnectionQueueMutex.Lock(); - for (unsigned int k=0; k < rakPeer->requestedConnectionQueue.Size(); k++) - { - if (rakPeer->requestedConnectionQueue[k]->systemAddress==systemAddress) - { - rakPeer->requestedConnectionQueue.RemoveAtIndex(k); - break; - } - } - rakPeer->requestedConnectionQueueMutex.Unlock(); - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Deleting client_handshake object %x and rcs->client_handshake object %x\n", client_handshake, rcs->client_handshake); - MafiaNet::OP_DELETE(client_handshake,_FILE_AND_LINE_); - MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); -#endif // LIBCAT_SECURITY - MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); - - break; - } - } - - if (unlock) - rakPeer->requestedConnectionQueueMutex.Unlock(); - - return true; - - } - else if ((unsigned char)(data)[0] == (MessageID)ID_CONNECTION_ATTEMPT_FAILED || - (unsigned char)(data)[0] == (MessageID)ID_NO_FREE_INCOMING_CONNECTIONS || - (unsigned char)(data)[0] == (MessageID)ID_CONNECTION_BANNED || - (unsigned char)(data)[0] == (MessageID)ID_ALREADY_CONNECTED || - (unsigned char)(data)[0] == (MessageID)ID_INVALID_PASSWORD || - (unsigned char)(data)[0] == (MessageID)ID_IP_RECENTLY_CONNECTED || - (unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) - { - - MafiaNet::BitStream bs((unsigned char*) data,length,false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - if ((unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) - bs.IgnoreBytes(sizeof(unsigned char)); - - RakNetGUID guid; - bs.Read(guid); - - RakPeer::RequestedConnectionStruct *rcs; - bool connectionAttemptCancelled=false; - rakPeer->requestedConnectionQueueMutex.Lock(); - for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) - { - rcs=rakPeer->requestedConnectionQueue[i]; - if (rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT && rcs->systemAddress==systemAddress) - { - connectionAttemptCancelled=true; - rakPeer->requestedConnectionQueue.RemoveAtIndex(i); - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Connection attempt canceled so deleting rcs->client_handshake object %x\n", rcs->client_handshake); - MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); -#endif // LIBCAT_SECURITY - MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); - break; - } - } - - rakPeer->requestedConnectionQueueMutex.Unlock(); - - if (connectionAttemptCancelled) - { - // Tell user of connection attempt failed - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = data[0]; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = systemAddress; - packet->guid=guid; - rakPeer->AddPacketToProducer(packet); - } - } - else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_1 && length > (int) (1+sizeof(OFFLINE_MESSAGE_DATA_ID))) - {/* - static int x = 0; - ++x; - - SystemAddress *addr = (SystemAddress*)&systemAddress; - addr->binaryAddress += x;*/ - - //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); - char remoteProtocol=data[1+sizeof(OFFLINE_MESSAGE_DATA_ID)]; - if (remoteProtocol!=RAKNET_PROTOCOL_VERSION) - { - MafiaNet::BitStream bs; - bs.Write((MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION); - bs.Write((unsigned char)RAKNET_PROTOCOL_VERSION); - bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*)bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); - - // SocketLayer::SendTo( rakNetSocket, (char*)bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - - RNS2_SendParameters bsp; - bsp.data = (char*) bs.GetData(); - bsp.length = bs.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - return true; - } - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - MafiaNet::BitStream bsOut; - bsOut.Write((MessageID)ID_OPEN_CONNECTION_REPLY_1); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); -#if LIBCAT_SECURITY==1 - if (rakPeer->_using_security) - { - bsOut.Write((unsigned char) 1); // HasCookie Yes - // Write cookie - uint32_t cookie = rakPeer->_cookie_jar->Generate(&systemAddress.address,sizeof(systemAddress.address)); - CAT_AUDIT_PRINTF("AUDIT: Writing cookie %i to %i:%i\n", cookie, systemAddress); - bsOut.Write(cookie); - // Write my public key - bsOut.WriteAlignedBytes((const unsigned char *) rakPeer->my_public_key,sizeof(rakPeer->my_public_key)); - } - else -#endif // LIBCAT_SECURITY - bsOut.Write((unsigned char) 0); // HasCookie oN - - // MTU. Lower MTU if it is exceeds our own limit - if (length+UDP_HEADER_SIZE > MAXIMUM_MTU_SIZE) - bsOut.WriteCasted(MAXIMUM_MTU_SIZE); - else - bsOut.WriteCasted(length+UDP_HEADER_SIZE); - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - // SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - - RNS2_SendParameters bsp; - bsp.data = (char*) bsOut.GetData(); - bsp.length = bsOut.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - } - else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_2) - { - SystemAddress bindingAddress; - RakNetGUID guid; - MafiaNet::BitStream bsOut; - MafiaNet::BitStream bs((unsigned char*) data, length, false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - - bool requiresSecurityOfThisClient=false; -#if LIBCAT_SECURITY==1 - char remoteHandshakeChallenge[cat::EasyHandshake::CHALLENGE_BYTES]; - - if (rakPeer->_using_security) - { - systemAddress.ToString(false, str1, static_cast(64)); - requiresSecurityOfThisClient=rakPeer->IsInSecurityExceptionList(str1)==false; - - uint32_t cookie; - bs.Read(cookie); - CAT_AUDIT_PRINTF("AUDIT: Got cookie %i from %i:%i\n", cookie, systemAddress); - if (rakPeer->_cookie_jar->Verify(&systemAddress.address,sizeof(systemAddress.address), cookie)==false) - { - return true; - } - CAT_AUDIT_PRINTF("AUDIT: Cookie good!\n"); - - unsigned char clientWroteChallenge; - bs.Read(clientWroteChallenge); - - if (requiresSecurityOfThisClient==true && clientWroteChallenge==0) - { - // Fail, client doesn't support security, and it is required - return true; - } - - if (clientWroteChallenge) - { - bs.ReadAlignedBytes((unsigned char*) remoteHandshakeChallenge, cat::EasyHandshake::CHALLENGE_BYTES); -#ifdef CAT_AUDIT - printf("AUDIT: RECV CHALLENGE "); - for (int ii = 0; ii < sizeof(remoteHandshakeChallenge); ++ii) - { - printf("%02x", (cat::u8)remoteHandshakeChallenge[ii]); - } - printf("\n"); -#endif - } - } -#endif // LIBCAT_SECURITY - - bs.Read(bindingAddress); - uint16_t mtu; - bs.Read(mtu); - bs.Read(guid); - - RakPeer::RemoteSystemStruct *rssFromSA = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - bool IPAddrInUse = rssFromSA != 0 && rssFromSA->isActive; - RakPeer::RemoteSystemStruct *rssFromGuid = rakPeer->GetRemoteSystemFromGUID(guid, true); - bool GUIDInUse = rssFromGuid != 0 && rssFromGuid->isActive; - - // IPAddrInUse, GuidInUse, outcome - // TRUE, , TRUE , ID_OPEN_CONNECTION_REPLY if they are the same, else ID_ALREADY_CONNECTED - // FALSE, , TRUE , ID_ALREADY_CONNECTED (someone else took this guid) - // TRUE, , FALSE , ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) - // FALSE , FALSE , Allow connection - - int outcome; - if (IPAddrInUse & GUIDInUse) - { - if (rssFromSA==rssFromGuid && rssFromSA->connectMode==RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER) - { - // ID_OPEN_CONNECTION_REPLY if they are the same - outcome=1; - - // Note to self: If REQUESTED_CONNECTION, this means two systems attempted to connect to each other at the same time, and one finished first. - // Returns ID)_CONNECTION_REQUEST_ACCEPTED to one system, and ID_ALREADY_CONNECTED followed by ID_NEW_INCOMING_CONNECTION to another - } - else - { - // ID_ALREADY_CONNECTED (restarted raknet, connected again from same ip, plus someone else took this guid) - outcome=2; - } - } - else if (IPAddrInUse==false && GUIDInUse==true) - { - // ID_ALREADY_CONNECTED (someone else took this guid) - outcome=3; - } - else if (IPAddrInUse==true && GUIDInUse==false) - { - // ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) - outcome=4; - } - else - { - // Allow connection - outcome=0; - } - - MafiaNet::BitStream bsAnswer; - bsAnswer.Write((MessageID)ID_OPEN_CONNECTION_REPLY_2); - bsAnswer.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsAnswer.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - bsAnswer.Write(systemAddress); - bsAnswer.Write(mtu); - bsAnswer.Write(requiresSecurityOfThisClient); - - if (outcome==1) - { - // Duplicate connection request packet from packetloss - // Send back the same answer -#if LIBCAT_SECURITY==1 - if (requiresSecurityOfThisClient) - { - CAT_AUDIT_PRINTF("AUDIT: Resending public key and answer from packetloss. Sending ID_OPEN_CONNECTION_REPLY_2\n"); - bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); - } -#endif // LIBCAT_SECURITY - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); - // SocketLayer::SendTo( rakNetSocket, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - - RNS2_SendParameters bsp; - bsp.data = (char*) bsAnswer.GetData(); - bsp.length = bsAnswer.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - - return true; - } - else if (outcome!=0) - { - bsOut.Write((MessageID)ID_ALREADY_CONNECTED); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(rakPeer->myGuid); - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - // SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - RNS2_SendParameters bsp; - bsp.data = (char*) bsOut.GetData(); - bsp.length = bsOut.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - - return true; - } - - if (rakPeer->AllowIncomingConnection(systemAddress)==false) - { - bsOut.Write((MessageID)ID_NO_FREE_INCOMING_CONNECTIONS); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(rakPeer->myGuid); - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - //SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - RNS2_SendParameters bsp; - bsp.data = (char*) bsOut.GetData(); - bsp.length = bsOut.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - - return true; - } - - bool thisIPConnectedRecently=false; - rssFromSA = rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, requiresSecurityOfThisClient); - - if (thisIPConnectedRecently==true) - { - bsOut.Write((MessageID)ID_IP_RECENTLY_CONNECTED); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(rakPeer->myGuid); - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - //SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - - RNS2_SendParameters bsp; - bsp.data = (char*) bsOut.GetData(); - bsp.length = bsOut.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - - return true; - } - -#if LIBCAT_SECURITY==1 - if (requiresSecurityOfThisClient) - { - CAT_AUDIT_PRINTF("AUDIT: Writing public key. Sending ID_OPEN_CONNECTION_REPLY_2\n"); - if (rakPeer->_server_handshake->ProcessChallenge(remoteHandshakeChallenge, rssFromSA->answer, rssFromSA->reliabilityLayer.GetAuthenticatedEncryption() )) - { - CAT_AUDIT_PRINTF("AUDIT: Challenge good!\n"); - // Keep going to OK block - } - else - { - CAT_AUDIT_PRINTF("AUDIT: Challenge BAD!\n"); - - // Unassign this remote system - rakPeer->DereferenceRemoteSystem(systemAddress); - return true; - } - - bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); - } -#endif // LIBCAT_SECURITY - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); - // SocketLayer::SendTo( rakNetSocket, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - RNS2_SendParameters bsp; - bsp.data = (char*) bsAnswer.GetData(); - bsp.length = bsAnswer.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - } - return true; - } - - return false; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// #med - deprecate this overload --- socketList[0] is not really ensured to be the correctly bound socket at all! -void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, MafiaNet::TimeUS timeRead, BitStream &updateBitStream ) -{ - ProcessNetworkPacket(systemAddress,data,length,rakPeer,rakPeer->socketList[0],timeRead, updateBitStream); -} -void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSocket2* rakNetSocket, MafiaNet::TimeUS timeRead, BitStream &updateBitStream ) -{ -#if LIBCAT_SECURITY==1 -#ifdef CAT_AUDIT - printf("AUDIT: RECV "); - for (int ii = 0; ii < length; ++ii) - { - printf("%02x", (cat::u8)data[ii]); - } - printf("\n"); -#endif -#endif // LIBCAT_SECURITY - - RakAssert(systemAddress.GetPort()); - bool isOfflineMessage; - if (ProcessOfflineNetworkPacket(systemAddress, data, length, rakPeer, rakNetSocket, &isOfflineMessage, timeRead)) - { - return; - } - -// MafiaNet::Packet *packet; - RakPeer::RemoteSystemStruct *remoteSystem; - - // See if this datagram came from a connected system - remoteSystem = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - if ( remoteSystem ) - { - // Handle regular incoming data - // HandleSocketReceiveFromConnectedPlayer is only safe to be called from the same thread as Update, which is this thread - if ( isOfflineMessage==false) - { - remoteSystem->reliabilityLayer.HandleSocketReceiveFromConnectedPlayer( - data, length, systemAddress, rakPeer->pluginListNTS, remoteSystem->MTUSize, - rakNetSocket, &rnr, timeRead, updateBitStream); - } - } - else - { - // int a=5; - // printf("--- Packet from unknown system %s\n", systemAddress.ToString()); - } -} - -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GenerateSeedFromGuid(void) -{ - /* - // Construct a random seed based on the initial guid value, and the last digits of the difference to each subsequent number - // This assumes that only the last 3 bits of each guidId integer has a meaningful amount of randomness between it and the prior number - unsigned int t = guid.g[0]; - unsigned int i; - for (i=1; i < sizeof(guid.g) / sizeof(guid.g[0]); i++) - { - unsigned int diff = guid.g[i]-guid.g[i-1]; - unsigned int diff3Bits = diff & 0x0007; - diff3Bits <<= 29; - diff3Bits >>= (i-1)*3; - t ^= diff3Bits; - } - - return t; - */ - return (unsigned int) ((myGuid.g >> 32) ^ myGuid.g); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DerefAllSockets(void) -{ - unsigned int i; - for (i=0; i < socketList.Size(); i++) - { - if (socketFactory && !socketList[i]->IsBerkleySocket()) - socketFactory->Destroy(socketList[i]); - else - MafiaNet::OP_DELETE(socketList[i], _FILE_AND_LINE_); - } - socketList.Clear(false, _FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetRakNetSocketFromUserConnectionSocketIndex(unsigned int userIndex) const -{ - unsigned int i; - for (i=0; i < socketList.Size(); i++) - { - if (socketList[i]->GetUserConnectionSocketIndex()==userIndex) - return i; - } - RakAssert("GetRakNetSocketFromUserConnectionSocketIndex failed" && 0); - return (unsigned int) -1; -} - -/* -// DS_APR -void RakPeer::ProcessChromePacket(RakNetSocket2 *s, const char *buffer, int dataSize, const SystemAddress& recvFromAddress, MafiaNet::TimeUS timeRead) -{ - RakAssert(buffer); - RakAssert(dataSize > 0); - RakAssert(recvFromAddress.GetPort()); - - RNS2RecvStruct *recvFromStruct; - recvFromStruct=bufferedPackets.Allocate( _FILE_AND_LINE_ ); - RakAssert(dataSize <= (int)sizeof(recvFromStruct->data)); - memcpy(recvFromStruct->data, buffer, dataSize); - recvFromStruct->bytesRead=dataSize; - recvFromStruct->systemAddress=recvFromAddress; - recvFromStruct->timeRead=timeRead; - bufferedPackets.Push(recvFromStruct); -} -*/ - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -/* -bool RakPeer::RunRecvFromOnce( RakNetSocket *s ) -{ - RakPeer::RecvFromStruct *recvFromStruct; - - recvFromStruct=bufferedPackets.Allocate( _FILE_AND_LINE_ ); - if (recvFromStruct != nullptr) - { - recvFromStruct->s=s; - SocketLayer::RecvFromBlocking(s, this, recvFromStruct->data, &recvFromStruct->bytesRead, &recvFromStruct->systemAddress, &recvFromStruct->timeRead); - - if (recvFromStruct->bytesRead>0) - { - RakAssert(recvFromStruct->systemAddress.GetPort()); - bufferedPackets.Push(recvFromStruct); - quitAndDataEvents.SetEvent(); - - // Got data - return true; - } - else - { - bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); - } - } - // No data - return false; -} -*/ -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::RunUpdateCycle(BitStream &updateBitStream ) -{ - RakPeer::RemoteSystemStruct * remoteSystem; - unsigned int activeSystemListIndex; - Packet *packet; - // int currentSentBytes,currentReceivedBytes; -// unsigned numberOfBytesUsed; -// BitSize_t numberOfBitsUsed; - //SystemAddress authoritativeClientSystemAddress; - BitSize_t bitSize; - unsigned int byteSize; - unsigned char *data; - SystemAddress systemAddress; - BufferedCommandStruct *bcs; - bool callerDataAllocationUsed; - RakNetStatistics *rnss; - MafiaNet::TimeUS timeNS=0; - MafiaNet::Time timeMS=0; - - // This is here so RecvFromBlocking actually gets data from the same thread - -#if defined(_WIN32) - // #high - review this - this is hardly correct to only work with the first socket here... - if (socketList[0]->GetSocketType()==RNS2T_WINDOWS && ((RNS2_Windows*)socketList[0])->GetSocketLayerOverride()) - { - int len; - SystemAddress sender; - char dataOut[ MAXIMUM_MTU_SIZE ]; - do { - len = ((RNS2_Windows*)socketList[0])->GetSocketLayerOverride()->RakNetRecvFrom(dataOut,&sender,true); - if (len>0) - ProcessNetworkPacket( sender, dataOut, len, this, socketList[0], MafiaNet::GetTimeUS(), updateBitStream ); - } while (len>0); - } -#endif - -// unsigned int socketListIndex; - RNS2RecvStruct *recvFromStruct; - while ((recvFromStruct=PopBufferedPacket())!=0) - { - /* - for (socketListIndex=0; socketListIndex < socketList.Size(); socketListIndex++) - { - if ((RakNetSocket*) socketList[socketListIndex]==recvFromStruct->s) - break; - } - if (socketListIndex!=socketList.Size()) - */ - ProcessNetworkPacket(recvFromStruct->systemAddress, recvFromStruct->data, recvFromStruct->bytesRead, this, recvFromStruct->socket, recvFromStruct->timeRead, updateBitStream); - DeallocRNS2RecvStruct(recvFromStruct, _FILE_AND_LINE_); - } - - while ((bcs=bufferedCommands.PopInaccurate())!=0) - { - if (bcs->command==BufferedCommandStruct::BCS_SEND) - { - // GetTime is a very slow call so do it once and as late as possible - if (timeNS==0) - { - timeNS = MafiaNet::GetTimeUS(); - timeMS = (MafiaNet::TimeMS)(timeNS/(MafiaNet::TimeUS)1000); - } - - callerDataAllocationUsed=SendImmediate((char*)bcs->data, bcs->numberOfBitsToSend, bcs->priority, bcs->reliability, bcs->orderingChannel, bcs->systemIdentifier, bcs->broadcast, true, timeNS, bcs->receipt); - if ( callerDataAllocationUsed==false ) - rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); - - // Set the new connection state AFTER we call sendImmediate in case we are setting it to a disconnection state, which does not allow further sends - if (bcs->connectionMode!=RemoteSystemStruct::NO_ACTION ) - { - remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); - if (remoteSystem) - remoteSystem->connectMode=bcs->connectionMode; - } - } - else if (bcs->command==BufferedCommandStruct::BCS_SESSION_ACCEPT || bcs->command==BufferedCommandStruct::BCS_SESSION_REJECT) - { - // Validate here rather than in the user-thread API: only the network thread may read connectMode - // and the awaiting flag without racing the receive path. - RemoteSystemStruct *sessionSystem = GetRemoteSystem( bcs->systemIdentifier, true, true ); - // connectMode is checked as well as the awaiting flag: a peer that disconnects while the decision - // is queued leaves the flag set (the teardown path does not clear session state), and answering - // then would send on a dying connection and report a connection for a peer that has already gone. - if (sessionSystem && sessionSystem->isActive && - sessionSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && - sessionSystem->sessionConfigAwaitingLocalDecision) - { - sessionSystem->sessionConfigAwaitingLocalDecision = false; - const unsigned int sessionPayloadLength = (unsigned int) BITS_TO_BYTES(bcs->numberOfBitsToSend); - if (bcs->command==BufferedCommandStruct::BCS_SESSION_ACCEPT) - { - // With a pending pool the handshake may have started while the server was full, so the - // incoming limit is enforced here. The application should check first; this is the backstop - // that keeps two accepts racing one free slot from both getting it. - SessionAdmission::Limits limits; - limits.maxIncoming = GetMaximumIncomingConnections(); - limits.maxPending = maximumPendingSessions; - limits.maxPendingPerAddress = maximumPendingSessionsPerAddress; - SessionAdmission::Counts counts; - CountRemoteInitiatedPeers(UNASSIGNED_SYSTEM_ADDRESS, counts.connected, counts.exchanging, counts.pending, counts.pendingFromAddress); - if (SessionAdmission::MayAccept(limits, counts)) - SendSessionConfigResponse(sessionSystem, bcs->data, sessionPayloadLength); - else - SendSessionConfigRejection(sessionSystem, 0, 0); - } - else - SendSessionConfigRejection(sessionSystem, bcs->data, sessionPayloadLength); - } - if (bcs->data) - rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); - } - else if (bcs->command==BufferedCommandStruct::BCS_SESSION_STATUS) - { - // Only a server peer still awaiting its application's decision may be told anything, and telling - // it restarts the session timer here as well as on the client when it arrives. - RemoteSystemStruct *sessionSystem = GetRemoteSystem( bcs->systemIdentifier, true, true ); - if (sessionSystem && sessionSystem->isActive && - sessionSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && - sessionSystem->sessionConfigAwaitingLocalDecision && - sessionSystem->sessionConfigIsConnectingSide==false) - { - MafiaNet::BitStream statusStream; - statusStream.Write((MessageID)ID_SESSION_CONFIG_STATUS); - const unsigned int statusLength = (unsigned int) BITS_TO_BYTES(bcs->numberOfBitsToSend); - if (bcs->data && statusLength > 0) - statusStream.Write(bcs->data, statusLength); - SendImmediate((char*)statusStream.GetData(), statusStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, sessionSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); - sessionSystem->sessionConfigStartTime = MafiaNet::GetTimeMS(); - } - if (bcs->data) - rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); - } - else if (bcs->command==BufferedCommandStruct::BCS_CLOSE_CONNECTION) - { - // bcs->socket may be null: the socket can be released between queuing this - // command and processing it here (e.g. during rapid connect/disconnect churn). - // RakAssert is a no-op in release (NDEBUG), so guard explicitly and fall back - // to the primary socket to avoid dereferencing null in CloseConnectionInternal2. - RakNetSocket2 *closeSocket = bcs->socket; - if (closeSocket == nullptr && socketList.Size() > 0) - closeSocket = socketList[0]; - if (closeSocket != nullptr) - CloseConnectionInternal2(bcs->systemIdentifier, false, true, bcs->orderingChannel, bcs->priority, *closeSocket); - } - else if (bcs->command==BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS) - { - // Reroute - RakPeer::RemoteSystemStruct *rssFromGuid = GetRemoteSystem(bcs->systemIdentifier.rakNetGuid,true,true); - if (rssFromGuid!=0) - { - unsigned int existingSystemIndex = GetRemoteSystemIndex(rssFromGuid->systemAddress); - ReferenceRemoteSystem(bcs->systemIdentifier.systemAddress, existingSystemIndex); - } - } - else if (bcs->command==BufferedCommandStruct::BCS_GET_SOCKET) - { - SocketQueryOutput *sqo; - if (bcs->systemIdentifier.IsUndefined()) - { - sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); - sqo->sockets=socketList; - socketQueryOutput.Push(sqo); - } - else - { - remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); - sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); - - sqo->sockets.Clear(false, _FILE_AND_LINE_); - if (remoteSystem) - { - sqo->sockets.Push(remoteSystem->rakNetSocket, _FILE_AND_LINE_ ); - } - else - { - // Leave empty smart pointer - } - socketQueryOutput.Push(sqo); - } - - } - -#ifdef _DEBUG - bcs->data=0; -#endif - - bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); - } - - if (requestedConnectionQueue.IsEmpty()==false) - { - if (timeNS==0) - { - timeNS = MafiaNet::GetTimeUS(); - timeMS = (MafiaNet::TimeMS)(timeNS/(MafiaNet::TimeUS)1000); - } - - bool condition1, condition2; - unsigned requestedConnectionQueueIndex=0; - requestedConnectionQueueMutex.Lock(); - while (requestedConnectionQueueIndex < requestedConnectionQueue.Size()) - { - RequestedConnectionStruct *rcs; - rcs = requestedConnectionQueue[requestedConnectionQueueIndex]; - requestedConnectionQueueMutex.Unlock(); - if (rcs->nextRequestTime < timeMS) - { - condition1=rcs->requestsMade==rcs->sendConnectionAttemptCount+1; - condition2=(bool)((rcs->systemAddress==UNASSIGNED_SYSTEM_ADDRESS)==1); - // If too many requests made or a hole then remove this if possible, otherwise invalidate it - if (condition1 || condition2) - { - if (rcs->data) - { - rakFree_Ex(rcs->data, _FILE_AND_LINE_ ); - rcs->data=0; - } - - if (condition1 && !condition2 && rcs->actionToTake==RequestedConnectionStruct::CONNECT) - { - // Tell user of connection attempt failed - packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - AddPacketToProducer(packet); - } - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Connection attempt FAILED so deleting rcs->client_handshake object %x\n", rcs->client_handshake); - MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); -#endif - MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); - - requestedConnectionQueueMutex.Lock(); - for (unsigned int k=0; k < requestedConnectionQueue.Size(); k++) - { - if (requestedConnectionQueue[k]==rcs) - { - requestedConnectionQueue.RemoveAtIndex(k); - break; - } - } - requestedConnectionQueueMutex.Unlock(); - } - else - { - - const unsigned attemptsPerMTU = GetConnectionAttemptsPerMTU(rcs->sendConnectionAttemptCount); - int MTUSizeIndex = rcs->requestsMade / attemptsPerMTU; - if (MTUSizeIndex>=NUM_MTU_SIZES) - MTUSizeIndex=NUM_MTU_SIZES-1; - rcs->requestsMade++; - rcs->nextRequestTime=timeMS+rcs->timeBetweenSendConnectionAttemptsMS; - - MafiaNet::BitStream bitStream; - //WriteOutOfBandHeader(&bitStream, ID_USER_PACKET_ENUM); - bitStream.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_1); - bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bitStream.Write((MessageID)RAKNET_PROTOCOL_VERSION); - bitStream.PadWithZeroToByteLength(mtuSizes[MTUSizeIndex]-UDP_HEADER_SIZE); - - char str[256]; - rcs->systemAddress.ToString(true,str,static_cast(256)); - - //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); - - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*) bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), rcs->systemAddress); - - RakNetSocket2 *socketToUse; - if (rcs->socket == 0) - socketToUse = socketList[rcs->socketIndex]; - else - socketToUse = rcs->socket; - - rcs->systemAddress.FixForIPVersion(socketToUse->GetBoundAddress()); - if (socketToUse->IsBerkleySocket()) - ((RNS2_Berkley*)socketToUse)->SetDoNotFragment(1); - -// SocketLayer::SetDoNotFragment(socketToUse, 1); - MafiaNet::Time sendToStart= MafiaNet::GetTime(); - - RNS2_SendParameters bsp; - bsp.data = (char*) bitStream.GetData(); - bsp.length = bitStream.GetNumberOfBytesUsed(); - bsp.systemAddress = rcs->systemAddress; - const RNS2SendResult sendResult = socketToUse->Send(&bsp, _FILE_AND_LINE_); - // Send() reports failure as sendto's own return value and drops the reason, so - // the error has to be read back off the thread before anything else touches a - // socket. The old test here compared that return value against 10040 directly, - // which sendto never yields -- it yields SOCKET_ERROR -- so this branch was dead - // and a locally-refused MTU burned its rung's entire attempt budget. - if (sendResult < 0 && RNS2_IsDatagramTooLargeError(RNS2_GetLastSocketError())) - { - // The local interface refused the datagram outright. Every attempt at this - // size fails the same way, so step down now. - rcs->requestsMade = (unsigned char) ((MTUSizeIndex + 1) * attemptsPerMTU); - rcs->nextRequestTime=timeMS; - } - else - { - MafiaNet::Time sendToEnd= MafiaNet::GetTime(); - if (sendToEnd-sendToStart>100) - { - // A send that blocks this long means the outgoing interface is - // struggling: a virtual VPN adapter renegotiating its tunnel, or a full - // transmit queue. Smaller datagrams are likelier to get through, so drop - // to the lowest MTU -- but keep trying. Giving up here (which is what - // this did when already on the lowest rung) turned a transient stall - // into ID_CONNECTION_ATTEMPT_FAILED with most of the budget unspent. - const int lowestMtuIndex = (int) (attemptsPerMTU * (unsigned) (NUM_MTU_SIZES - 1)); - if (lowestMtuIndex > (int) rcs->requestsMade) - { - rcs->requestsMade = (unsigned char) lowestMtuIndex; - rcs->nextRequestTime=timeMS; - } - } - } - // SocketLayer::SetDoNotFragment(socketToUse, 0); - if (socketToUse->IsBerkleySocket()) - ((RNS2_Berkley*)socketToUse)->SetDoNotFragment(0); - - requestedConnectionQueueIndex++; - } - } - else - requestedConnectionQueueIndex++; - - requestedConnectionQueueMutex.Lock(); - } - requestedConnectionQueueMutex.Unlock(); - } - - // remoteSystemList in network thread - for ( activeSystemListIndex = 0; activeSystemListIndex < activeSystemListSize; ++activeSystemListIndex ) - //for ( remoteSystemIndex = 0; remoteSystemIndex < remoteSystemListSize; ++remoteSystemIndex ) - { - // I'm using systemAddress from remoteSystemList but am not locking it because this loop is called very frequently and it doesn't - // matter if we miss or do an extra update. The reliability layers themselves never care which player they are associated with - //systemAddress = remoteSystemList[ remoteSystemIndex ].systemAddress; - // Allow the systemAddress for this remote system list to change. We don't care if it changes now. - // remoteSystemList[ remoteSystemIndex ].allowSystemAddressAssigment=true; - - - // Found an active remote system - remoteSystem = activeSystemList[ activeSystemListIndex ]; - systemAddress = remoteSystem->systemAddress; - RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); - // Update is only safe to call from the same thread that calls HandleSocketReceiveFromConnectedPlayer, - // which is this thread - - if (timeNS==0) - { - timeNS = MafiaNet::GetTimeUS(); - timeMS = (MafiaNet::TimeMS)(timeNS/(MafiaNet::TimeUS)1000); - //RAKNET_DEBUG_PRINTF("timeNS = %I64i timeMS=%i\n", timeNS, timeMS); - } - - - if (timeMS > remoteSystem->lastReliableSend && timeMS-remoteSystem->lastReliableSend > remoteSystem->reliabilityLayer.GetTimeoutTime()/2 && remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) - { - // If no reliable packets are waiting for an ack, do a one byte reliable send so that disconnections are noticed - RakNetStatistics rakNetStatistics; - rnss=remoteSystem->reliabilityLayer.GetStatistics(&rakNetStatistics); - if (rnss->messagesInResendBuffer==0) - { - PingInternal( systemAddress, true, MafiaNet::Reliability::Reliable ); - - //remoteSystem->lastReliableSend=timeMS+remoteSystem->reliabilityLayer.GetTimeoutTime(); - remoteSystem->lastReliableSend=timeMS; - } - } - - if (endThreads) - // for the final call, make sure we send out any outstanding ACKs - remoteSystem->reliabilityLayer.UpdateAndForceACKs( remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream ); // systemAddress only used for the internet simulator test - else - remoteSystem->reliabilityLayer.Update( remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream ); // systemAddress only used for the internet simulator test - - // The reliability layer steps the negotiated MTU down when it - // detects an in-session path-MTU black hole; keep the value - // GetMTUSize() reports in sync with what is actually on the wire. - // Written only on an actual step-down: GetMTUSize() reads this - // field from the user thread without synchronization (as it always - // has for the connect-time write), so avoid turning it into a - // continuously-written field. - const int reliabilityMtu = remoteSystem->reliabilityLayer.GetCurrentMtuBytes(); - if (remoteSystem->MTUSize != reliabilityMtu) - remoteSystem->MTUSize = reliabilityMtu; - - // Check for failure conditions - if ( remoteSystem->reliabilityLayer.IsDeadConnection() || - ((remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY) && remoteSystem->reliabilityLayer.IsOutgoingDataWaiting()==false) || - (remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK && (remoteSystem->reliabilityLayer.AreAcksWaiting()==false || remoteSystem->reliabilityLayer.AckTimeout(timeMS)==true)) || - (( - (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || - remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || - remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) - && timeMS > remoteSystem->connectionTime && timeMS - remoteSystem->connectionTime > 10000)) - ) - { - // RAKNET_DEBUG_PRINTF("timeMS=%i remoteSystem->connectionTime=%i\n", timeMS, remoteSystem->connectionTime ); - - // Before the close notification logic: a held session request is not a reported connection, but - // the application is holding a decision for it and has to learn it will never be needed. - ProduceSessionAbandoned(remoteSystem); - - // Failed. Inform the user? - // TODO - RakNet 4.0 - Return a different message identifier for DISCONNECT_ASAP_SILENTLY and DISCONNECT_ASAP than for DISCONNECT_ON_NO_ACK - // The first two mean we called CloseConnection(), the last means the other system sent us ID_DISCONNECTION_NOTIFICATION - // A connection the application was never told about must not produce a close notification for - // it. On the accepting side that covers a peer refused by RejectSession() (which parks the slot - // in DISCONNECT_ON_NO_ACK so the refusal is delivered first) and one dropped mid-handshake. - // The connecting side is exempt: it has an outstanding Connect() to resolve either way. - const bool reportableConnection = remoteSystem->connectionReportedToApplication || remoteSystem->weInitiatedTheConnection; - if (reportableConnection && - (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED || remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION - || remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA - || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK)) - { - -// MafiaNet::BitStream undeliveredMessages; -// remoteSystem->reliabilityLayer.GetUndeliveredMessages(&undeliveredMessages,remoteSystem->MTUSize); - -// packet=AllocPacket(sizeof( char ) + undeliveredMessages.GetNumberOfBytesUsed()); - // A graceful remote disconnect (DISCONNECT_ON_NO_ACK) may carry a reason payload stashed when the - // notification arrived. Only ID_DISCONNECTION_NOTIFICATION carries it; ID_CONNECTION_LOST and - // ID_CONNECTION_ATTEMPT_FAILED are locally synthesized and stay payload-less. - const bool attachReason = remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK && remoteSystem->disconnectReasonData!=0; - const unsigned int reasonLength = attachReason ? remoteSystem->disconnectReasonLength : 0; - packet=AllocPacket(sizeof( char ) + reasonLength, _FILE_AND_LINE_); - if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) - packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't - else if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA) - // Dropped mid-session-handshake. The connecting side still has an attempt outstanding; the - // accepting side never reported anything, so a lost connection is all it can report. - packet->data[ 0 ] = remoteSystem->sessionConfigIsConnectingSide - ? (unsigned char)ID_CONNECTION_ATTEMPT_FAILED : (unsigned char)ID_CONNECTION_LOST; - else if (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) - packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection - else - packet->data[ 0 ] = ID_DISCONNECTION_NOTIFICATION; // DeadConnection - - if (attachReason) - memcpy(packet->data + sizeof(unsigned char), remoteSystem->disconnectReasonData, reasonLength); - -// memcpy(packet->data+1, undeliveredMessages.GetData(), undeliveredMessages.GetNumberOfBytesUsed()); - - packet->guid = remoteSystem->guid; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - - AddPacketToProducer(packet); - } - // else connection shutting down, don't bother telling the user - -#ifdef _DO_PRINTF - RAKNET_DEBUG_PRINTF("Connection dropped for player %i:%i\n", systemAddress); -#endif - // we are about to close the connection to the remote system, we'd still make sure to send any outstanding ACKs, so for the remote system not unnecessarily waiting for these until its timeout - // (and trying to resend them unnecessarily) - remoteSystem->reliabilityLayer.UpdateAndForceACKs(remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream); - - CloseConnectionInternal2(systemAddress, false, true, 0, MafiaNet::Priority::Low, *(remoteSystem->rakNetSocket)); - continue; - } - - // A session handshake that never completes must not pin the slot. Acks keep flowing in - // EXCHANGING_SESSION_DATA, so the ordinary dead-connection detection above would never fire; bound - // it by the same timeout instead. Interactive servers put an application in the middle of this - // exchange, so it is a reachable state, not a theoretical one. - if ( remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && - remoteSystem->sessionConfigStartTime!=0 && - timeMS > remoteSystem->sessionConfigStartTime && - timeMS - remoteSystem->sessionConfigStartTime > GetSessionTimeoutFor(remoteSystem) ) - { - // Only the connecting side reports a failure; a server just drops the half-open slot. - if (remoteSystem->sessionConfigIsConnectingSide) - { - Packet *timeoutPacket=AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); - timeoutPacket->data[0]=(unsigned char)ID_CONNECTION_ATTEMPT_FAILED; - timeoutPacket->bitSize=sizeof(MessageID)*8; - timeoutPacket->systemAddress=remoteSystem->systemAddress; - timeoutPacket->systemAddress.systemIndex=remoteSystem->remoteSystemIndex; - timeoutPacket->guid=remoteSystem->guid; - timeoutPacket->guid.systemIndex=timeoutPacket->systemAddress.systemIndex; - AddPacketToProducer(timeoutPacket); - } - else - { - ProduceSessionAbandoned(remoteSystem); - } - - remoteSystem->reliabilityLayer.UpdateAndForceACKs(remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream); - CloseConnectionInternal2(systemAddress, false, true, 0, MafiaNet::Priority::Low, *(remoteSystem->rakNetSocket)); - continue; - } - - // Ping this guy if it is time to do so - if ( remoteSystem->connectMode==RemoteSystemStruct::CONNECTED && timeMS > remoteSystem->nextPingTime && ( occasionalPing || remoteSystem->lowestPing == (unsigned short)-1 ) ) - { - remoteSystem->nextPingTime = timeMS + 5000; - PingInternal( systemAddress, true, MafiaNet::Reliability::Unreliable ); - - // Update again immediately after this tick so the ping goes out right away - quitAndDataEvents.SetEvent(); - } - - // Find whoever has the lowest player ID - //if (systemAddress < authoritativeClientSystemAddress) - // authoritativeClientSystemAddress=systemAddress; - - // Does the reliability layer have any packets waiting for us? - // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer - bitSize = remoteSystem->reliabilityLayer.Receive( &data ); - - while ( bitSize > 0 ) - { - // These types are for internal use and should never arrive from a network packet - if (data[0]==ID_CONNECTION_ATTEMPT_FAILED) - { - RakAssert(0); - bitSize=0; - continue; - } - - // Fast and easy - just use the data that was returned - byteSize = (unsigned int) BITS_TO_BYTES( bitSize ); - - // For unknown senders we only accept a few specific packets - if (remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) - { - if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) - { - ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else - { - CloseConnectionInternal2(systemAddress, false, true, 0, MafiaNet::Priority::Low, *(remoteSystem->rakNetSocket)); -#ifdef _DO_PRINTF - RAKNET_DEBUG_PRINTF("Temporarily banning %i:%i for sending nonsense data\n", systemAddress); -#endif - - char str1[64]; - systemAddress.ToString(false, str1, static_cast(64)); - AddToBanList(str1, remoteSystem->reliabilityLayer.GetTimeoutTime()); - - - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else - { - // However, if we are connected we still take a connection request in case both systems are trying to connect to each other - // at the same time - if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) - { - // 04/27/06 This is wrong. With cross connections, we can both have initiated the connection are in state REQUESTED_CONNECTION - // 04/28/06 Downgrading connections from connected will close the connection due to security at ((remoteSystem->connectMode!=RemoteSystemStruct::CONNECTED && time > remoteSystem->connectionTime && time - remoteSystem->connectionTime > 10000)) - if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) - { - ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); - } - else - { - - MafiaNet::BitStream bs((unsigned char*) data,byteSize,false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - bs.IgnoreBytes(RakNetGUID::size()); - MafiaNet::Time incomingTimestamp; - bs.Read(incomingTimestamp); - - // Got a connection request message from someone we are already connected to. Just reply normally. - // This can happen due to race conditions with the fully connected mesh - OnConnectionRequest( remoteSystem, incomingTimestamp ); - } - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char) data[ 0 ] == ID_NEW_INCOMING_CONNECTION && byteSize > sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(MafiaNet::Time)*2 ) - { - if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) - { - // Transport is up, but the application is not told so until the client's session payload - // arrives in ID_SESSION_CONFIG_REQUEST and we answer it. - remoteSystem->connectMode=RemoteSystemStruct::EXCHANGING_SESSION_DATA; - remoteSystem->sessionConfigIsConnectingSide=false; - remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); - PingInternal( systemAddress, true, MafiaNet::Reliability::Unreliable ); - - // Update again immediately after this tick so the ping goes out right away - quitAndDataEvents.SetEvent(); - - MafiaNet::BitStream inBitStream((unsigned char *) data, byteSize, false); - SystemAddress bsSystemAddress; - - inBitStream.IgnoreBits(8); - inBitStream.Read(bsSystemAddress); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); - - MafiaNet::Time sendPingTime, sendPongTime; - inBitStream.Read(sendPingTime); - inBitStream.Read(sendPongTime); - OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); - - // Overwrite the data in the packet - // NewIncomingConnectionStruct newIncomingConnectionStruct; - // MafiaNet::BitStream nICS_BS( data, NewIncomingConnectionStruct_Size, false ); - // newIncomingConnectionStruct.Deserialize( nICS_BS ); - - remoteSystem->myExternalSystemAddress = bsSystemAddress; - - // Bug: If A connects to B through R, A's firstExternalID is set to R. If A tries to send to R, sends to loopback because R==firstExternalID - // Correct fix is to specify in Connect() if target is through a proxy. - // However, in practice you have to connect to something else first anyway to know about the proxy. So setting once only is good enough - if (firstExternalID==UNASSIGNED_SYSTEM_ADDRESS) - { - firstExternalID=bsSystemAddress; - firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); - } - - // Hold this until the session handshake completes; the bytes are copied because `data` - // is freed as soon as this branch returns. - ClearSessionConfig(remoteSystem); - remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); - remoteSystem->withheldConnectionPacketData = (unsigned char*) rakMalloc_Ex(byteSize, _FILE_AND_LINE_); - if (remoteSystem->withheldConnectionPacketData!=0) - { - memcpy(remoteSystem->withheldConnectionPacketData, data, byteSize); - remoteSystem->withheldConnectionPacketLength = byteSize; - } - // AllocPacket used to take ownership of `data` here; the copy above means this branch does. - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else - { - // Send to game even if already connected. This could happen when connecting to 127.0.0.1 - // Ignore, already connected - // rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else if ( (unsigned char) data[ 0 ] == ID_CONNECTED_PONG && byteSize == sizeof(unsigned char)+sizeof(MafiaNet::Time)*2 ) - { - MafiaNet::Time sendPingTime, sendPongTime; - - // Copy into the ping times array the current time - the value returned - // First extract the sent ping - MafiaNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); - //PingStruct ps; - //ps.Deserialize(psBS); - inBitStream.IgnoreBits(8); - inBitStream.Read(sendPingTime); - inBitStream.Read(sendPongTime); - - OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); - - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char)data[0] == ID_CONNECTED_PING && byteSize == sizeof(unsigned char)+sizeof(MafiaNet::Time) ) - { - MafiaNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); - inBitStream.IgnoreBits(8); - MafiaNet::Time sendPingTime; - inBitStream.Read(sendPingTime); - - MafiaNet::BitStream outBitStream; - outBitStream.Write((MessageID)ID_CONNECTED_PONG); - outBitStream.Write(sendPingTime); - outBitStream.Write(MafiaNet::GetTime()); - SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Unreliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0 ); - - // Update again immediately after this tick so the ping goes out right away - quitAndDataEvents.SetEvent(); - - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char) data[ 0 ] == ID_DISCONNECTION_NOTIFICATION ) - { - // Stash any reason payload (everything after the 1-byte ID) so it can ride along with the - // user-facing notification packet synthesized once outstanding ACKs are flushed. This raw - // reliability-layer buffer is freed below, so the bytes have to be copied out now. - ClearDisconnectReason(remoteSystem); - if (byteSize > sizeof(unsigned char)) - { - const unsigned int reasonLength = byteSize - (unsigned int) sizeof(unsigned char); - remoteSystem->disconnectReasonData = (unsigned char*) rakMalloc_Ex(reasonLength, _FILE_AND_LINE_); - if (remoteSystem->disconnectReasonData != 0) - { - memcpy(remoteSystem->disconnectReasonData, data + sizeof(unsigned char), reasonLength); - remoteSystem->disconnectReasonLength = reasonLength; - } - } - - // We shouldn't close the connection immediately because we need to ack the ID_DISCONNECTION_NOTIFICATION - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ON_NO_ACK; - rakFree_Ex(data, _FILE_AND_LINE_ ); - - // AddPacketToProducer(packet); - } - else if ( (unsigned char)(data)[0] == ID_DETECT_LOST_CONNECTIONS && byteSize == sizeof(unsigned char) ) - { - // Do nothing - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG_REQUEST ) - { - // Server side of the session handshake. The client's payload arrives before we answer, so a - // server can inspect it and refuse without ever having disclosed its own. - if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigAwaitingLocalDecision==false) - { - if (StoreRemoteSessionConfig(remoteSystem, (const unsigned char*)data + sizeof(MessageID), byteSize - (unsigned int)sizeof(MessageID))==false) - { - // Oversized payload is a protocol violation; drop without answering. - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if (sessionConfigInteractive) - { - // Let the application decide. The connection stays unreported until it calls - // AcceptSession() or RejectSession(), or the handshake times out below. - remoteSystem->sessionConfigAwaitingLocalDecision=true; - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - } - else - { - rakPeerMutexes[ sessionConfig_Mutex ].Lock(); - SendSessionConfigResponse(remoteSystem, (const char*)sessionConfig.GetData(), (unsigned int)sessionConfig.GetNumberOfBytesUsed()); - rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else - { - // A repeat request, or one on an already-established connection: ignore it rather than - // letting a peer re-run the handshake and re-produce the connection packet. - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG ) - { - // Client side of the session handshake: the server's payload is the last thing we needed, so - // the withheld ID_CONNECTION_REQUEST_ACCEPTED goes out now and the connection becomes real. - // Only the peer that initiated the connection may be answered this way, so a client cannot - // push a payload at a server by replying with the server's own message. - if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigIsConnectingSide) - { - if (StoreRemoteSessionConfig(remoteSystem, (const unsigned char*)data + sizeof(MessageID), byteSize - (unsigned int)sizeof(MessageID))==false) - { - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - } - else - { - remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; - remoteSystem->sessionConfigStartTime=0; - ProduceWithheldConnectionPacket(remoteSystem, (MessageID)ID_CONNECTION_REQUEST_ACCEPTED); - } - } - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG_STATUS ) - { - // Client side, while the server's application decides: surface the status and restart our own - // session timer, which is what lets a queued client wait longer than the timeout. Bound to the - // connecting side like the other replies, so a client cannot push one at a server. - if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigIsConnectingSide) - { - remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - } - else - { - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG_REJECTED ) - { - // Only meaningful to the peer that initiated the connection. Without this a malicious client - // could inject ID_CONNECTION_ATTEMPT_FAILED -- a message the application only ever expects for - // its own outbound connects -- into a listening server's queue. - if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigIsConnectingSide) - { - // Rewrite in place so the reason string the server supplied stays at data+1, matching the - // ID_DISCONNECTION_NOTIFICATION reason convention. No connection is ever reported. - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->data[0]=(unsigned char)ID_CONNECTION_ATTEMPT_FAILED; - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - } - else - { - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else if ( (unsigned char)(data)[0] == ID_INVALID_PASSWORD ) - { - if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) - { - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - } - else - { - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST_ACCEPTED ) - { - if (byteSize > sizeof(MessageID)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(SystemIndex)+sizeof(MafiaNet::Time)*2) - { - // Make sure this connection accept is from someone we wanted to connect to - bool allowConnection, alreadyConnected; - - if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || - remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || - allowConnectionResponseIPMigration) - allowConnection=true; - else - allowConnection=false; - - if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) - alreadyConnected=true; - else - alreadyConnected=false; - - if ( allowConnection ) - { - SystemAddress externalID; - SystemIndex systemIndex; -// SystemAddress internalID; - - MafiaNet::BitStream inBitStream((unsigned char *) data, byteSize, false); - inBitStream.IgnoreBits(8); - // inBitStream.Read(remotePort); - inBitStream.Read(externalID); - inBitStream.Read(systemIndex); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); - - MafiaNet::Time sendPingTime, sendPongTime; - inBitStream.Read(sendPingTime); - inBitStream.Read(sendPongTime); - OnConnectedPong(sendPingTime, sendPongTime, remoteSystem); - - // Find a free remote system struct to use - // MafiaNet::BitStream casBitS(data, byteSize, false); - // ConnectionAcceptStruct cas; - // cas.Deserialize(casBitS); - // systemAddress.GetPort() = remotePort; - - // The remote system told us our external IP, so save it - remoteSystem->myExternalSystemAddress = externalID; - // Transport is up, but the application is not told so until the session payloads have been - // exchanged. ID_CONNECTION_REQUEST_ACCEPTED is stashed and replayed from ID_SESSION_CONFIG. - remoteSystem->connectMode=RemoteSystemStruct::EXCHANGING_SESSION_DATA; - remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); - - // Bug: If A connects to B through R, A's firstExternalID is set to R. If A tries to send to R, sends to loopback because R==firstExternalID - // Correct fix is to specify in Connect() if target is through a proxy. - // However, in practice you have to connect to something else first anyway to know about the proxy. So setting once only is good enough - if (firstExternalID==UNASSIGNED_SYSTEM_ADDRESS) - { - firstExternalID=externalID; - firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); - } - - // Hold the connection-complete packet until the session handshake finishes. The bytes are - // copied because `data` is freed as soon as this branch returns. - ClearSessionConfig(remoteSystem); - remoteSystem->sessionConfigIsConnectingSide=true; - remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); - remoteSystem->withheldConnectionPacketData = (unsigned char*) rakMalloc_Ex(byteSize, _FILE_AND_LINE_); - if (remoteSystem->withheldConnectionPacketData!=0) - { - memcpy(remoteSystem->withheldConnectionPacketData, data, byteSize); - remoteSystem->withheldConnectionPacketLength = byteSize; - } - // AllocPacket used to take ownership of `data` here; the copy above means this branch does. - rakFree_Ex(data, _FILE_AND_LINE_ ); - - MafiaNet::BitStream outBitStream; - outBitStream.Write((MessageID)ID_NEW_INCOMING_CONNECTION); - outBitStream.Write(systemAddress); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - outBitStream.Write(ipList[i]); - outBitStream.Write(sendPongTime); - outBitStream.Write(MafiaNet::GetTime()); - - SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0 ); - - // Our half of the session handshake. Sent on the same ordered channel right behind - // ID_NEW_INCOMING_CONNECTION, so the server has the transport connection before this lands. - { - MafiaNet::BitStream sessionBitStream; - sessionBitStream.Write((MessageID)ID_SESSION_CONFIG_REQUEST); - rakPeerMutexes[ sessionConfig_Mutex ].Lock(); - if (sessionConfig.GetNumberOfBytesUsed() > 0) - sessionBitStream.Write((const char*)sessionConfig.GetData(), sessionConfig.GetNumberOfBytesUsed()); - rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); - SendImmediate( (char*)sessionBitStream.GetData(), sessionBitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0 ); - } - - if (alreadyConnected==false) - { - PingInternal( systemAddress, true, MafiaNet::Reliability::Unreliable ); - } - } - else - { - // Ignore, already connected - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else - { - // Version mismatch error? - RakAssert(0); - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else - { - // What do I do if I get a message from a system, before I am fully connected? - // I can either ignore it or give it to the user - // It seems like giving it to the user is a better option - // - // Except while the session handshake is running: the application has not been told this - // connection exists, so handing it application data from that peer would deliver a packet - // for a guid it has never seen connect -- and from a peer it may still refuse. A peer that - // respects the protocol cannot send here anyway: its own public send path is gated on the - // same state, so anything arriving in this window is malformed or hostile. - if ((data[0]>=(MessageID)ID_TIMESTAMP || data[0]==ID_SND_RECEIPT_ACKED || data[0]==ID_SND_RECEIPT_LOSS) && - remoteSystem->isActive && - remoteSystem->connectMode!=RemoteSystemStruct::EXCHANGING_SESSION_DATA - ) - { - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - if (!DispatchNetworkThreadReceive(packet)) - AddPacketToProducer(packet); - } - else - { - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - } - - // Does the reliability layer have any more packets waiting for us? - // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer - bitSize = remoteSystem->reliabilityLayer.Receive( &data ); - } - - } - - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void RakPeer::OnRNS2Recv(RNS2RecvStruct *recvStruct) -{ - if (incomingDatagramEventHandler) - { - if (incomingDatagramEventHandler(recvStruct)!=true) - return; - } - - PushBufferedPacket(recvStruct); - quitAndDataEvents.SetEvent(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RAK_THREAD_DECLARATION(MafiaNet::UpdateNetworkLoop) -{ - - - - RakPeer * rakPeer = ( RakPeer * ) arguments; - - -/* - // 11/15/05 - this is slower than Sleep() -#ifdef _WIN32 -#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) - // Lets see if these timers give better performance than Sleep - HANDLE timerHandle; - LARGE_INTEGER dueTime; - - if ( rakPeer->threadSleepTimer <= 0 ) - rakPeer->threadSleepTimer = 1; - - // 2nd parameter of false means synchronization timer instead of manual-reset timer - timerHandle = CreateWaitableTimer( nullptr, FALSE, 0 ); - - RakAssert( timerHandle ); - - dueTime.QuadPart = -10000 * rakPeer->threadSleepTimer; // 10000 is 1 ms? - - BOOL success = SetWaitableTimer( timerHandle, &dueTime, rakPeer->threadSleepTimer, nullptr, nullptr, FALSE ); - (void) success; - RakAssert( success ); - -#endif -#endif -*/ - - BitStream updateBitStream( MAXIMUM_MTU_SIZE -#if LIBCAT_SECURITY==1 - + cat::AuthenticatedEncryption::OVERHEAD_BYTES -#endif - ); -// - rakPeer->isMainLoopThreadActive = true; - - bool running = true; - while ( running ) - { - if (rakPeer->endThreads) { - // allow just one more final update-run prior to shutting down this thread to make sure that outstanding acks are still sent and the peers don't unnecessary wait for already retrieved packets - // note: this fixes part of SLNET-123 - running = false; - } -// #ifdef _DEBUG -// // Sanity check, make sure RunUpdateCycle does not block or not otherwise get called for a long time -// RakNetTime thisCall=MafiaNet::GetTime(); -// RakAssert(thisCall-lastCall<250); -// lastCall=thisCall; -// #endif - if (rakPeer->userUpdateThreadPtr) - rakPeer->userUpdateThreadPtr(rakPeer, rakPeer->userUpdateThreadData); - - rakPeer->RunUpdateCycle(updateBitStream); - - // Pending sends go out this often, unless quitAndDataEvents is set - rakPeer->quitAndDataEvents.WaitOnEvent(10); - - /* - -// #if ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && -#if defined(USE_WAIT_FOR_MULTIPLE_EVENTS) && defined(_WIN32) - - if (rakPeer->threadSleepTimer>0) - { - WSAEVENT eventArray[256]; - unsigned int i, eventArrayIndex; - for (i=0,eventArrayIndex=0; i < rakPeer->socketList.Size(); i++) - { - if (rakPeer->socketList[i]->recvEvent!=INVALID_HANDLE_VALUE) - { - eventArray[eventArrayIndex]=rakPeer->socketList[i]->recvEvent; - eventArrayIndex++; - if (eventArrayIndex==256) - break; - } - } - WSAWaitForMultipleEvents(eventArrayIndex,(const HANDLE*) &eventArray,FALSE,rakPeer->threadSleepTimer,FALSE); - } - else - { - RakSleep(0); - } - -#else // ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) - #pragma message("-- RakNet: Using Sleep(). Uncomment USE_WAIT_FOR_MULTIPLE_EVENTS in defines.h if you want to use WaitForSingleObject instead. --") - - RakSleep( rakPeer->threadSleepTimer ); -#endif - */ - } - - rakPeer->isMainLoopThreadActive = false; - - /* -#ifdef _WIN32 -#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) - CloseHandle(timerHandle); -#endif -#endif - */ - - - - - return 0; - -} - -void RakPeer::CallPluginCallbacks(DataStructures::List &pluginList, Packet *packet) -{ - for (unsigned int i=0; i < pluginList.Size(); i++) - { - switch (packet->data[0]) - { - case ID_DISCONNECTION_NOTIFICATION: - pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_DISCONNECTION_NOTIFICATION); - break; - case ID_CONNECTION_LOST: - pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_CONNECTION_LOST); - break; - case ID_NEW_INCOMING_CONNECTION: - pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, true); - break; - case ID_CONNECTION_REQUEST_ACCEPTED: - pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, false); - break; - case ID_CONNECTION_ATTEMPT_FAILED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_ATTEMPT_FAILED); - break; - case ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); - break; - case ID_OUR_SYSTEM_REQUIRES_SECURITY: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_OUR_SYSTEM_REQUIRES_SECURITY); - break; - case ID_PUBLIC_KEY_MISMATCH: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_PUBLIC_KEY_MISMATCH); - break; - case ID_ALREADY_CONNECTED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_ALREADY_CONNECTED); - break; - case ID_NO_FREE_INCOMING_CONNECTIONS: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_NO_FREE_INCOMING_CONNECTIONS); - break; - case ID_CONNECTION_BANNED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_BANNED); - break; - case ID_INVALID_PASSWORD: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INVALID_PASSWORD); - break; - case ID_INCOMPATIBLE_PROTOCOL_VERSION: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INCOMPATIBLE_PROTOCOL); - break; - case ID_IP_RECENTLY_CONNECTED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_IP_RECENTLY_CONNECTED); - break; - } - } -} - -void RakPeer::FillIPList(void) -{ - if (ipList[0]!=UNASSIGNED_SYSTEM_ADDRESS) - return; - - // Fill out ipList structure - RakNetSocket2::GetMyIP( ipList ); - - // Sort the addresses from lowest to highest - int startingIdx = 0; - while (startingIdx < MAXIMUM_NUMBER_OF_INTERNAL_IDS-1 && ipList[startingIdx] != UNASSIGNED_SYSTEM_ADDRESS) - { - int lowestIdx = startingIdx; - for (int curIdx = startingIdx + 1; curIdx < MAXIMUM_NUMBER_OF_INTERNAL_IDS-1 && ipList[curIdx] != UNASSIGNED_SYSTEM_ADDRESS; curIdx++ ) - { - if (ipList[curIdx] < ipList[startingIdx]) - { - lowestIdx = curIdx; - } - } - if (startingIdx != lowestIdx) - { - SystemAddress temp = ipList[startingIdx]; - ipList[startingIdx] = ipList[lowestIdx]; - ipList[lowestIdx] = temp; - } - ++startingIdx; - } -} - - -// #if defined(RMO_NEW_UNDEF_ALLOCATING_QUEUE) -// #pragma pop_macro("new") -// #undef RMO_NEW_UNDEF_ALLOCATING_QUEUE -// #endif - +/* + * Original work: Copyright (c) 2014, Oculus VR, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * RakNet License.txt file in the licenses directory of this source tree. An additional grant + * of patent rights can be found in the RakNet Patents.txt file in the same directory. + * + * + * Modified work: Copyright (c) 2016-2020, SLikeSoft UG (haftungsbeschränkt) + * + * This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style + * license found in the license.txt file in the root directory of this source tree. + */ + +// \file +// + + + +#define CAT_NEUTER_EXPORT /* Neuter dllimport for libcat */ + +#include "mafianet/defines.h" +#include "mafianet/peer.h" +#include "mafianet/session_admission.h" +#include "mafianet/types.h" + +#ifdef _WIN32 + +#else +#include +#endif + +// #if defined(new) +// #pragma push_macro("new") +// #undef new +// #define RMO_NEW_UNDEF_ALLOCATING_QUEUE +// #endif + +#include +#include // toupper +#include +#include "mafianet/get_time.h" +#include +#include +#include "mafianet/message_identifiers.h" +#include "mafianet/mtu_black_hole.h" +#include "mafianet/ds_huffman_encoding_tree.h" +#include "mafianet/rand.h" +#include "mafianet/plugin_interface2.h" +#include "mafianet/string_compressor.h" +#include "mafianet/string_table.h" +#include "mafianet/network_id_object.h" +#include "mafianet/types.h" +#include "mafianet/dr_sha1.h" +#include "mafianet/sleep.h" +#include "mafianet/assert.h" +#include "mafianet/version.h" +#include "mafianet/network_id_manager.h" +#include "mafianet/get_time_of_day.h" +#include "mafianet/signaled_event.h" +#include "mafianet/super_fast_hash.h" +#include "mafianet/alloca.h" +#include "mafianet/wsa_startup_singleton.h" +#include "mafianet/linux_adapter.h" +#include "mafianet/osx_adapter.h" + +#ifdef USE_THREADED_SEND +#include "mafianet/send_to_thread.h" +#endif + +#ifdef CAT_AUDIT +#define CAT_AUDIT_PRINTF(...) printf(__VA_ARGS__) +#else +#define CAT_AUDIT_PRINTF(...) +#endif + +namespace MafiaNet +{ +RAK_THREAD_DECLARATION(UpdateNetworkLoop); +RAK_THREAD_DECLARATION(RecvFromLoop); +RAK_THREAD_DECLARATION(UDTConnect); +} +#define REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE 8 + +#if !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ ) +#include // malloc +#endif + + + +#if defined(_WIN32) +// +#else +/* +#include // Console 2 +#include +extern bool _extern_Console2LoadModules(void); +extern int _extern_Console2GetConnectionStatus(void); +extern int _extern_Console2GetLobbyStatus(void); +//extern bool Console2StartupFluff(unsigned int *); +extern void Console2ShutdownFluff(void); +//extern unsigned int Console2ActivateConnection(unsigned int, void *); +//extern bool Console2BlockOnEstablished(void); +extern void Console2GetIPAndPort(unsigned int, char *, unsigned short *, unsigned int ); +//extern void Console2DeactivateConnection(unsigned int, unsigned int); +*/ +#endif + + +// Probed high to low while connecting: the connecting peer pads +// ID_OPEN_CONNECTION_REQUEST_1 to a rung and steps down when nothing comes back, +// so the negotiated MTU is the largest rung that survived the path. Each rung is +// a whole IP datagram, MAXIMUM_MTU_SIZE first. +// +// 1280 is both the IPv6 minimum MTU and what WireGuard-derived tunnels commonly +// settle on, which makes it the single most valuable intermediate rung; 1024 +// covers heavier or stacked encapsulation; 576 is the dial-up floor. The gap +// from MAXIMUM_MTU_SIZE straight to 1200 that used to sit here meant a peer one +// byte over the top rung gave up ~20% of its payload capacity to find that out. +// +// The ladder itself lives in mtu_black_hole.h: the in-session black-hole +// step-down (ReliabilityLayer) walks the same rungs this handshake probes. +static const int NUM_MTU_SIZES=MafiaNet::MTU_LADDER_SIZE; +static const int *const mtuSizes=MafiaNet::MTU_LADDER; + +// How many connection attempts are spent on each rung of mtuSizes before +// stepping down. +// +// Never zero. Connect() takes sendConnectionAttemptCount from the caller and +// documents no lower bound, so a caller asking for fewer attempts than there are +// rungs used to divide by zero here -- a crash that was unreachable only because +// the default (12) happened to exceed NUM_MTU_SIZES. Such a caller now gets one +// attempt per rung and stops when its budget runs out, having tried the largest +// sizes first. +static unsigned GetConnectionAttemptsPerMTU(unsigned sendConnectionAttemptCount) +{ + const unsigned attemptsPerMTU = sendConnectionAttemptCount / (unsigned) NUM_MTU_SIZES; + return attemptsPerMTU > 0 ? attemptsPerMTU : 1; +} + + +// Note to self - if I change this it might affect RECIPIENT_OFFLINE_MESSAGE_INTERVAL in Natpunchthrough.cpp +//static const int MAX_OPEN_CONNECTION_REQUESTS=8; +//static const int TIME_BETWEEN_OPEN_CONNECTION_REQUESTS=500; + +using namespace MafiaNet; + +static RakNetRandom rnr; + +/* +struct RakPeerAndIndex +{ + RakNetSocket2 *s; + RakPeer *rakPeer; +}; +*/ + +static const unsigned int MAX_OFFLINE_DATA_LENGTH=400; // I set this because I limit ID_CONNECTION_REQUEST to 512 bytes, and the password is appended to that packet. + +// Used to distinguish between offline messages with data, and messages from the reliability layer +// Should be different than any message that could result from messages from the reliability layer +// Make sure highest bit is 0, so isValid in DatagramHeaderFormat is false +static const unsigned char OFFLINE_MESSAGE_DATA_ID[16]={0x00,0xFF,0xFF,0x00,0xFE,0xFE,0xFE,0xFE,0xFD,0xFD,0xFD,0xFD,0x12,0x34,0x56,0x78}; + +struct PacketFollowedByData +{ + Packet p; + unsigned char data[1]; +}; + +Packet *RakPeer::AllocPacket(unsigned dataSize, const char *file, unsigned int line) +{ + // Crashes when dataSize is 4 bytes - not sure why +// unsigned char *data = (unsigned char *) rakMalloc_Ex(sizeof(PacketFollowedByData)+dataSize, file, line); +// Packet *p = &((PacketFollowedByData *)data)->p; +// p->data=((PacketFollowedByData *)data)->data; +// p->length=dataSize; +// p->bitSize=BYTES_TO_BITS(dataSize); +// p->deleteData=false; +// p->guid=UNASSIGNED_RAKNET_GUID; +// return p; + + MafiaNet::Packet *p; + packetAllocationPoolMutex.Lock(); + p = packetAllocationPool.Allocate(file,line); + ++packetsOutstanding; + packetAllocationPoolMutex.Unlock(); + p = new ((void*)p) Packet; + p->data=(unsigned char*) rakMalloc_Ex(dataSize,file,line); + p->length=dataSize; + p->bitSize=BYTES_TO_BITS(dataSize); + p->deleteData=true; + p->guid=UNASSIGNED_RAKNET_GUID; + p->wasGeneratedLocally=false; + return p; +} + +Packet *RakPeer::AllocPacket(unsigned dataSize, unsigned char *data, const char *file, unsigned int line) +{ + // Packet *p = (Packet *)rakMalloc_Ex(sizeof(Packet), file, line); + MafiaNet::Packet *p; + packetAllocationPoolMutex.Lock(); + p = packetAllocationPool.Allocate(file,line); + ++packetsOutstanding; + packetAllocationPoolMutex.Unlock(); + p = new ((void*)p) Packet; + RakAssert(p); + p->data=data; + p->length=dataSize; + p->bitSize=BYTES_TO_BITS(dataSize); + p->deleteData=true; + p->guid=UNASSIGNED_RAKNET_GUID; + p->wasGeneratedLocally=false; + return p; +} + +STATIC_FACTORY_DEFINITIONS(RakPeerInterface,RakPeer) + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Constructor +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RakPeer() +{ +#if LIBCAT_SECURITY==1 + // Encryption and security + CAT_AUDIT_PRINTF("AUDIT: Initializing RakPeer security flags: using_security = false, server_handshake = null, cookie_jar = null\n"); + _using_security = false; + _server_handshake = 0; + _cookie_jar = 0; +#endif + + StringCompressor::AddReference(); + MafiaNet::StringTable::AddReference(); + WSAStartupSingleton::AddRef(); + + defaultMTUSize = mtuSizes[NUM_MTU_SIZES-1]; + trackFrequencyTable = false; + maximumIncomingConnections = 0; + maximumNumberOfPeers = 0; + //remoteSystemListSize=0; + remoteSystemList = 0; + activeSystemList = 0; + activeSystemListSize=0; + remoteSystemLookup=0; + bytesSentPerSecond = bytesReceivedPerSecond = 0; + endThreads = true; + networkThreadReceiveStopped = false; + isMainLoopThreadActive = false; + socketFactory = nullptr; + manualUpdate = false; + incomingDatagramEventHandler=0; + sessionConfigInteractive=false; + maximumPendingSessions=0; + maximumPendingSessionsPerAddress=0; + sessionTimeout=0; + + + + + + // isRecvfromThreadActive=false; +#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 + occasionalPing = true; +#else + occasionalPing = false; +#endif + allowInternalRouting=false; + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + ipList[i]=UNASSIGNED_SYSTEM_ADDRESS; + allowConnectionResponseIPMigration = false; + //incomingPasswordLength=outgoingPasswordLength=0; + incomingPasswordLength=0; + splitMessageProgressInterval=0; + //unreliableTimeout=0; + unreliableTimeout=1000; + maxOutgoingBPS=0; + firstExternalID=UNASSIGNED_SYSTEM_ADDRESS; + myGuid=UNASSIGNED_RAKNET_GUID; + userUpdateThreadPtr=0; + userUpdateThreadData=0; + +#ifdef _DEBUG + // Wait longer to disconnect in debug so I don't get disconnected while tracing + defaultTimeoutTime=30000; +#else + defaultTimeoutTime=10000; +#endif + +#ifdef _DEBUG + _packetloss=0.0; + _minExtraPing=0; + _extraPingVariance=0; +#endif + + bufferedCommands.SetPageSize(sizeof(BufferedCommandStruct)*16); + socketQueryOutput.SetPageSize(sizeof(SocketQueryOutput)*8); + + packetAllocationPoolMutex.Lock(); + packetAllocationPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); + packetsOutstanding = 0; + packetAllocationPoolMutex.Unlock(); + shutdownGeneration = 0; + + remoteSystemIndexPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GenerateGUID(); + + quitAndDataEvents.InitEvent(); + limitConnectionFrequencyFromTheSameIP=false; + ResetSendReceipt(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Destructor +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::~RakPeer() +{ + Shutdown( 0, 0 ); + + // Free the ban list. + ClearBanList(); + + StringCompressor::RemoveReference(); + MafiaNet::StringTable::RemoveReference(); + WSAStartupSingleton::Deref(); + + quitAndDataEvents.CloseEvent(); + +#if LIBCAT_SECURITY==1 + // Encryption and security + CAT_AUDIT_PRINTF("AUDIT: Deleting RakPeer security objects, handshake = %x, cookie jar = %x\n", _server_handshake, _cookie_jar); + if (_server_handshake) MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + if (_cookie_jar) MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); +#endif + + + + + + + + + + + + + + + + +// for (unsigned int i=0; i < pluginListTS.Size(); i++) +// pluginListTS[i]->SetRakPeerInterface(0); +// for (unsigned int i=0; i < pluginListNTS.Size(); i++) +// pluginListNTS[i]->SetRakPeerInterface(0); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// \brief Starts the network threads, opens the listen port. +// You must call this before calling Connect(). +// Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). +// \note Call SetMaximumIncomingConnections if you want to accept incoming connections +// \param[in] maxConnections The maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.- A hybrid would set it to the sum of both types of connections +// \param[in] localPort The port to listen for connections on. +// \param[in] _threadSleepTimer How many ms to Sleep each internal update cycle. With new congestion control, the best results will be obtained by passing 10. +// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass &socketDescriptor, 1SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); +// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. +// \return False on failure (can't create socket or thread), true on success. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +StartupResult RakPeer::Startup( unsigned int maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority ) +{ + if (IsActive()) + return RAKNET_ALREADY_STARTED; + + SetNetworkThreadReceiveStopped(false); + + // If getting the guid failed in the constructor, try again + if (myGuid.g==0) + { + GenerateGUID(); + if (myGuid.g==0) + return COULD_NOT_GENERATE_GUID; + } + + if (threadPriority==-99999) + { + + +#if defined(_WIN32) + threadPriority=0; + + + + +#else + threadPriority=1000; +#endif + } + + + FillIPList(); + + if (myGuid==UNASSIGNED_RAKNET_GUID) + { + rnr.SeedMT( GenerateSeedFromGuid() ); + } + + //RakPeerAndIndex rpai[32]; + //RakAssert(socketDescriptorCount<32); + + RakAssert(socketDescriptors && socketDescriptorCount>=1); + + if (socketDescriptors==0 || socketDescriptorCount<1) + return INVALID_SOCKET_DESCRIPTORS; + + //unsigned short localPort; + //localPort=socketDescriptors[0].port; + + RakAssert( maxConnections > 0 ); + + if ( maxConnections <= 0 ) + return INVALID_MAX_CONNECTIONS; + + DerefAllSockets(); + + + unsigned i; + // Go through all socket descriptors and precreate sockets on the specified addresses + for (i=0; iCreate(socketDescriptors[i], i) : RakNetSocket2Allocator::AllocRNS2(); + if (r2 == nullptr) + { + DerefAllSockets(); + return SOCKET_PORT_ALREADY_IN_USE; + } + r2->SetUserConnectionSocketIndex(i); + if (r2->IsBerkleySocket()) + { + RNS2_BerkleyBindParameters bbp; + bbp.port=socketDescriptors[i].port; + bbp.hostAddress=(char*) socketDescriptors[i].hostAddress; + bbp.addressFamily=socketDescriptors[i].socketFamily; + bbp.type=SOCK_DGRAM; + bbp.protocol=socketDescriptors[i].extraSocketOptions; + bbp.nonBlockingSocket=false; + bbp.setBroadcast=true; + bbp.setIPHdrIncl=false; + bbp.doNotFragment=false; + bbp.pollingThreadPriority=threadPriority; + bbp.eventHandler=this; + RNS2BindResult br = ((RNS2_Berkley*) r2)->Bind(&bbp, _FILE_AND_LINE_); + + if ( + #if RAKNET_SUPPORT_IPV6==0 + socketDescriptors[i].socketFamily!=AF_INET || + #endif + br==BR_REQUIRES_RAKNET_SUPPORT_IPV6_DEFINE) + { + RakNetSocket2Allocator::DeallocRNS2(r2); + DerefAllSockets(); + return SOCKET_FAMILY_NOT_SUPPORTED; + } + else if (br==BR_FAILED_TO_BIND_SOCKET) + { + RakNetSocket2Allocator::DeallocRNS2(r2); + DerefAllSockets(); + return SOCKET_PORT_ALREADY_IN_USE; + } + else if (br==BR_FAILED_SEND_TEST) + { + RakNetSocket2Allocator::DeallocRNS2(r2); + DerefAllSockets(); + return SOCKET_FAILED_TEST_SEND; + } + else + { + RakAssert(br==BR_SUCCESS); + } + } + else + { + // Factory-supplied: already bound; it delivers datagrams to us. + r2->SetRecvEventHandler(this); + } + + socketList.Push(r2, _FILE_AND_LINE_ ); + + } + + for (i=0; iIsBerkleySocket()) + ((RNS2_Berkley*) socketList[i])->CreateRecvPollingThread(threadPriority); + } + + for (i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + { + if (ipList[i]==UNASSIGNED_SYSTEM_ADDRESS) + break; + // #high - using the 1st socket here is flawed - in cases of having multiple sockets (f.e. different ports and different families (i.e. IPv4/IPv6) we must use the proper + // socket for each IP address in the list + if (socketList[0]->IsBerkleySocket()) + { + unsigned short port = ((RNS2_Berkley*)socketList[0])->GetBoundAddress().GetPort(); + ipList[i].SetPortHostOrder(port); + + } + else + { + ipList[i].SetPortHostOrder(socketList[0]->GetBoundAddress().GetPort()); + } + } + + if ( maximumNumberOfPeers == 0 ) + { + // Don't allow more incoming connections than we have peers. + if ( maximumIncomingConnections > maxConnections ) + maximumIncomingConnections = maxConnections; + + maximumNumberOfPeers = maxConnections; + // 04/19/2006 - Don't overallocate because I'm no longer allowing connected pings. + // The disconnects are not consistently processed and the process was sloppy and complicated. + // Allocate 10% extra to handle new connections from players trying to connect when the server is full + //remoteSystemListSize = maxConnections;// * 11 / 10 + 1; + + // remoteSystemList in Single thread + //remoteSystemList = MafiaNet::OP_NEW( _FILE_AND_LINE_ ); + remoteSystemList = MafiaNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); + + remoteSystemLookup = MafiaNet::OP_NEW_ARRAY((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE, _FILE_AND_LINE_ ); + + activeSystemList = MafiaNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); + + for ( i = 0; i < maximumNumberOfPeers; i++ ) + //for ( i = 0; i < remoteSystemListSize; i++ ) + { + // remoteSystemList in Single thread + remoteSystemList[ i ].isActive = false; + remoteSystemList[ i ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; + remoteSystemList[ i ].guid = UNASSIGNED_RAKNET_GUID; + remoteSystemList[ i ].myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; + remoteSystemList[ i ].connectMode=RemoteSystemStruct::NO_ACTION; + remoteSystemList[ i ].MTUSize = defaultMTUSize; + remoteSystemList[ i ].remoteSystemIndex = (SystemIndex) i; + // One-time zero-init: the array is OP_NEW_ARRAY allocated with no member init, so prime the + // reason-payload fields here before ClearDisconnectReason() can ever free them. + remoteSystemList[ i ].disconnectReasonData = 0; + remoteSystemList[ i ].disconnectReasonLength = 0; + remoteSystemList[ i ].remoteSessionConfigData = 0; + remoteSystemList[ i ].remoteSessionConfigLength = 0; + remoteSystemList[ i ].withheldConnectionPacketData = 0; + remoteSystemList[ i ].withheldConnectionPacketLength = 0; + remoteSystemList[ i ].sessionConfigAwaitingLocalDecision = false; + remoteSystemList[ i ].sessionConfigIsConnectingSide = false; + remoteSystemList[ i ].connectionReportedToApplication = false; + remoteSystemList[ i ].sessionConfigStartTime = 0; +#ifdef _DEBUG + remoteSystemList[ i ].reliabilityLayer.ApplyNetworkSimulator(_packetloss, _minExtraPing, _extraPingVariance); +#endif + + // All entries in activeSystemList have valid pointers all the time. + activeSystemList[ i ] = &remoteSystemList[ i ]; + } + + for (i=0; i < (unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE; i++) + { + remoteSystemLookup[i]=0; + } + } + + // For histogram statistics + // nextReadBytesTime=0; + // lastSentBytes=lastReceivedBytes=0; + + if ( endThreads ) + { + updateCycleIsRunning = false; + endThreads = false; + firstExternalID=UNASSIGNED_SYSTEM_ADDRESS; + + ClearBufferedCommands(); + ClearBufferedPackets(); + ClearSocketQueryOutput(); + + if ( isMainLoopThreadActive == false && !manualUpdate ) + { +#if RAKPEER_USER_THREADED!=1 + + int errorCode; + + + + + + + + errorCode = MafiaNet::RakThread::Create(UpdateNetworkLoop, this, threadPriority); + + + if ( errorCode != 0 ) + { + Shutdown( 0, 0 ); + return FAILED_TO_CREATE_NETWORK_THREAD; + } +// RakAssert(isRecvFromLoopThreadActive.GetValue()==0); +#endif // RAKPEER_USER_THREADED!=1 + + /* + for (i=0; i(_FILE_AND_LINE_); + rpai->s=socketList[i]; + rpai->rakPeer=this; + +#if RAKPEER_USER_THREADED!=1 + + errorCode = MafiaNet::RakThread::Create(RecvFromLoop, rpai, threadPriority); + + if ( errorCode != 0 ) + { + Shutdown( 0, 0 ); + return FAILED_TO_CREATE_NETWORK_THREAD; + } +#endif // RAKPEER_USER_THREADED!=1 + } + */ + + + /* +#if RAKPEER_USER_THREADED!=1 + + while ( isRecvFromLoopThreadActive.GetValue() < (uint32_t) socketDescriptorCount ) + RakSleep(10); + #endif // RAKPEER_USER_THREADED!=1 + */ + + } + +#if RAKPEER_USER_THREADED!=1 + // Wait for the threads to activate. When they are active they will set these variables to true + while ( !manualUpdate && isMainLoopThreadActive == false ) + RakSleep(10); +#endif // RAKPEER_USER_THREADED!=1 + } + + for (i=0; i < pluginListTS.Size(); i++) + { + pluginListTS[i]->OnRakPeerStartup(); + } + + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginListNTS[i]->OnRakPeerStartup(); + } + +#ifdef USE_THREADED_SEND + MafiaNet::SendToThread::AddRef(); +#endif + + return RAKNET_STARTED; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Must be called while offline +// +// If you accept connections, you must call this or else security will not be enabled for incoming connections. +// +// This feature requires more round trips, bandwidth, and CPU time for the connection handshake +// x64 builds require under 25% of the CPU time of other builds +// +// See the Encryption sample for example usage +// +// Parameters: +// publicKey = A pointer to the public key for accepting new connections +// privateKey = A pointer to the private key for accepting new connections +// If the private keys are 0, then a new key will be generated when this function is called +// bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::InitializeSecurity(const char *public_key, const char *private_key, bool bRequireClientKey) +{ +#if LIBCAT_SECURITY==1 + if ( endThreads == false ) + return false; + + // Copy client public key requirement flag + _require_client_public_key = bRequireClientKey; + + if (_server_handshake) + { + CAT_AUDIT_PRINTF("AUDIT: Deleting old server_handshake %x\n", _server_handshake); + MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + } + if (_cookie_jar) + { + CAT_AUDIT_PRINTF("AUDIT: Deleting old cookie jar %x\n", _cookie_jar); + MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); + } + + _server_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); + _cookie_jar = MafiaNet::OP_NEW(_FILE_AND_LINE_); + + CAT_AUDIT_PRINTF("AUDIT: Created new server_handshake %x\n", _server_handshake); + CAT_AUDIT_PRINTF("AUDIT: Created new cookie jar %x\n", _cookie_jar); + CAT_AUDIT_PRINTF("AUDIT: Running _server_handshake->Initialize()\n"); + + if (_server_handshake->Initialize(public_key, private_key)) + { + CAT_AUDIT_PRINTF("AUDIT: Successfully initialized, filling cookie jar with goodies, storing public key and setting using security flag to true\n"); + + _server_handshake->FillCookieJar(_cookie_jar); + + memcpy(my_public_key, public_key, sizeof(my_public_key)); + + _using_security = true; + return true; + } + + CAT_AUDIT_PRINTF("AUDIT: Failure to initialize so deleting server handshake and cookie jar; also setting using_security flag = false\n"); + + MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + _server_handshake=0; + MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); + _cookie_jar=0; + _using_security = false; + return false; +#else + (void) public_key; + (void) private_key; + (void) bRequireClientKey; + + return false; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description +// Must be called while offline +// Disables security for incoming connections. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DisableSecurity( void ) +{ +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: DisableSecurity() called, so deleting _server_handshake %x and cookie_jar %x\n", _server_handshake, _cookie_jar); + MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + _server_handshake=0; + MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); + _cookie_jar=0; + + _using_security = false; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AddToSecurityExceptionList(const char *ip) +{ + securityExceptionMutex.Lock(); + securityExceptionList.Insert(RakString(ip), _FILE_AND_LINE_); + securityExceptionMutex.Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RemoveFromSecurityExceptionList(const char *ip) +{ + if (securityExceptionList.Size()==0) + return; + + if (ip==0) + { + securityExceptionMutex.Lock(); + securityExceptionList.Clear(false, _FILE_AND_LINE_); + securityExceptionMutex.Unlock(); + } + else + { + unsigned i=0; + securityExceptionMutex.Lock(); + while (i < securityExceptionList.Size()) + { + if (securityExceptionList[i].IPAddressMatch(ip)) + { + securityExceptionList[i]=securityExceptionList[securityExceptionList.Size()-1]; + securityExceptionList.RemoveAtIndex(securityExceptionList.Size()-1); + } + else + i++; + } + securityExceptionMutex.Unlock(); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsInSecurityExceptionList(const char *ip) +{ + if (securityExceptionList.Size()==0) + return false; + + unsigned i=0; + securityExceptionMutex.Lock(); + for (; i < securityExceptionList.Size(); i++) + { + if (securityExceptionList[i].IPAddressMatch(ip)) + { + securityExceptionMutex.Unlock(); + return true; + } + } + securityExceptionMutex.Unlock(); + return false; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sets how many incoming connections are allowed. If this is less than the number of players currently connected, no +// more players will be allowed to connect. If this is greater than the maximum number of peers allowed, it will be reduced +// to the maximum number of peers allowed. Defaults to 0. +// +// Parameters: +// numberAllowed - Maximum number of incoming connections allowed. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetMaximumIncomingConnections( unsigned short numberAllowed ) +{ + maximumIncomingConnections = numberAllowed; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the maximum number of incoming connections, which is always <= maxConnections +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetMaximumIncomingConnections( void ) const +{ + return maximumIncomingConnections; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns how many open connections there are at this time +// \return the number of open connections +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned short RakPeer::NumberOfConnections(void) const +{ + DataStructures::List addresses; + DataStructures::List guids; + GetSystemList(addresses, guids); + return (unsigned short) addresses.Size(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sets the password incoming connections must match in the call to Connect (defaults to none) +// Pass 0 to passwordData to specify no password +// +// Parameters: +// passwordData: A data block that incoming connections must match. This can be just a password, or can be a stream of data. +// - Specify 0 for no password data +// passwordDataLength: The length in bytes of passwordData +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetIncomingPassword( const char* passwordData, int passwordDataLength ) +{ + //if (passwordDataLength > MAX_OFFLINE_DATA_LENGTH) + // passwordDataLength=MAX_OFFLINE_DATA_LENGTH; + + if (passwordDataLength > 255) + passwordDataLength=255; + + if (passwordData==0) + passwordDataLength=0; + + // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? + // It won't overflow at least because incomingPasswordLength is an unsigned char + if (passwordDataLength>0) + memcpy(incomingPassword, passwordData, passwordDataLength); + incomingPasswordLength=(unsigned char)passwordDataLength; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetIncomingPassword( char* passwordData, int *passwordDataLength ) +{ + if (passwordData==0) + { + *passwordDataLength=incomingPasswordLength; + return; + } + + if (*passwordDataLength > incomingPasswordLength) + *passwordDataLength=incomingPasswordLength; + + if (*passwordDataLength>0) + memcpy(passwordData, incomingPassword, *passwordDataLength); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Call this to connect to the specified host (ip or domain name) and server port. +// Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. Calling both acts as a true peer. +// This is a non-blocking connection. You know the connection is successful when IsConnected() returns true +// or receive gets a packet with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. If the connection is not +// successful, such as rejected connection or no response then neither of these things will happen. +// Requires that you first call Initialize +// +// Parameters: +// host: Either a dotted IP address or a domain name +// remotePort: Which port to connect to on the remote machine. +// passwordData: A data block that must match the data block on the server. This can be just a password, or can be a stream of data +// passwordDataLength: The length in bytes of passwordData +// +// Returns: +// True on successful initiation. False on incorrect parameters, internal error, or too many existing peers +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +ConnectionAttemptResult RakPeer::Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime ) +{ + // If endThreads is true here you didn't call Startup() first. + if ( host == 0 || endThreads || connectionSocketIndex>=socketList.Size() ) + return INVALID_PARAMETER; + + RakAssert(remotePort!=0); + + connectionSocketIndex=GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + + if (passwordDataLength>255) + passwordDataLength=255; + + if (passwordData==0) + passwordDataLength=0; + + // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? + // It won't overflow at least because outgoingPasswordLength is an unsigned char +// if (passwordDataLength>0) +// memcpy(outgoingPassword, passwordData, passwordDataLength); +// outgoingPasswordLength=(unsigned char) passwordDataLength; + + // 04/02/09 - Can't remember why I disabled connecting to self, but it seems to work + // Connecting to ourselves in the same instance of the program? +// if ( ( strcmp( host, "127.0.0.1" ) == 0 || strcmp( host, "0.0.0.0" ) == 0 ) && remotePort == mySystemAddress[0].port ) +// return false; + + return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, connectionSocketIndex, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +ConnectionAttemptResult RakPeer::ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSocket2* socket, PublicKey *publicKey, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime) +{ + if ( host == 0 || endThreads || socket == 0 ) + return INVALID_PARAMETER; + + if (passwordDataLength>255) + passwordDataLength=255; + + if (passwordData==0) + passwordDataLength=0; + + return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, 0, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime, socket ); + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Stops the network threads and close all connections. Multiple calls are ok. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::Shutdown( unsigned int blockDuration, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority ) +{ + unsigned i,j; + bool anyActive; + MafiaNet::TimeMS startWaitingTime; +// SystemAddress systemAddress; + MafiaNet::TimeMS time; + //unsigned short systemListSize = remoteSystemListSize; // This is done for threading reasons + unsigned int systemListSize = maximumNumberOfPeers; + + ++shutdownGeneration; + + if ( blockDuration > 0 ) + { + for ( i = 0; i < systemListSize; i++ ) + { + // remoteSystemList in user thread + if (remoteSystemList[i].isActive) + NotifyAndFlagForShutdown(remoteSystemList[i].systemAddress, false, orderingChannel, disconnectionNotificationPriority); + } + + time = MafiaNet::GetTimeMS(); + startWaitingTime = time; + // Nothing else advances a manually updated peer (or its clock, under a + // test's virtual time): flush the notifications once and stop. + if (manualUpdate) + RunManualUpdate(); + while ( !manualUpdate && time - startWaitingTime < blockDuration ) + { + anyActive=false; + for (j=0; j < systemListSize; j++) + { + // remoteSystemList in user thread + if (remoteSystemList[j].isActive) + { + anyActive=true; + break; + } + } + + // If this system is out of packets to send, then stop waiting + if ( anyActive==false ) + break; + + // This will probably cause the update thread to run which will probably + // send the disconnection notification + + RakSleep(15); + time = MafiaNet::GetTimeMS(); + } + } + // Plugins tear down in OnRakPeerShutdown while the update thread may still be running; stop + // offering them messages there first, so no hook is mid-call or starts after this point. + SetNetworkThreadReceiveStopped(true); + + for (i=0; i < pluginListTS.Size(); i++) + { + pluginListTS[i]->OnRakPeerShutdown(); + } + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginListNTS[i]->OnRakPeerShutdown(); + } + + quitAndDataEvents.SetEvent(); + + endThreads = true; + +// MafiaNet::TimeMS timeout; +#if RAKPEER_USER_THREADED!=1 + + for (i=0; i < socketList.Size(); i++) + { + if (socketList[i]->IsBerkleySocket()) + { + ((RNS2_Berkley *)socketList[i])->SignalStopRecvPollingThread(); + } + } + + /* + // Get recvfrom to unblock + for (i=0; i < socketList.Size(); i++) + { + if (SocketLayer::SendTo(socketList[i], (const char*) &i,1,socketList[i]->GetBoundAddress(), _FILE_AND_LINE_)!=0) + break; + } + */ + + while ( isMainLoopThreadActive ) + { + RakSleep(15); + } + + activeSystemListSize = 0; + + /* + timeout = MafiaNet::GetTimeMS()+1000; + while ( isRecvFromLoopThreadActive.GetValue()>0 && MafiaNet::GetTimeMS()GetBoundAddress(), _FILE_AND_LINE_); + } + + RakSleep(30); + } + */ + + for (i=0; i < socketList.Size(); i++) + { + if (socketList[i]->IsBerkleySocket()) + { + ((RNS2_Berkley *)socketList[i])->BlockOnStopRecvPollingThread(); + } + } + + +#endif // RAKPEER_USER_THREADED!=1 + +// char c=0; +// unsigned int socketIndex; + // remoteSystemList in Single thread + for ( i = 0; i < systemListSize; i++ ) + { + // Reserve this reliability layer for ourselves + remoteSystemList[ i ].isActive = false; + + // Remove any remaining packets + RakAssert(remoteSystemList[ i ].MTUSize <= MAXIMUM_MTU_SIZE); + remoteSystemList[ i ].reliabilityLayer.Reset(false, remoteSystemList[ i ].MTUSize, false); + remoteSystemList[ i ].rakNetSocket = 0; + ClearDisconnectReason(&remoteSystemList[ i ]); + ClearSessionConfig(&remoteSystemList[ i ]); + } + + + // Setting maximumNumberOfPeers to 0 allows remoteSystemList to be reallocated in Initialize. + // Setting remoteSystemListSize prevents threads from accessing the reliability layer + maximumNumberOfPeers = 0; + //remoteSystemListSize = 0; + + // Free any packets the user didn't deallocate + packetReturnMutex.Lock(); + for (i=0; i < packetReturnQueue.Size(); i++) + DeallocatePacket(packetReturnQueue[i]); + packetReturnQueue.Clear(_FILE_AND_LINE_); + packetReturnMutex.Unlock(); + // Packets still out live in the pool's pages: a Shutdown() from inside Receive's dispatch, + // or from code handling a packet Receive returned, must not free the packet under it. The + // pages are reused by the next session and freed by a later Shutdown() or the destructor. + packetAllocationPoolMutex.Lock(); + if (packetsOutstanding == 0) + packetAllocationPool.Clear(_FILE_AND_LINE_); + packetAllocationPoolMutex.Unlock(); + + /* + if (isRecvFromLoopThreadActive.GetValue()>0) + { + timeout = MafiaNet::GetTimeMS()+1000; + while ( isRecvFromLoopThreadActive.GetValue()>0 && MafiaNet::GetTimeMS() addresses; + DataStructures::List guids; + GetSystemList(addresses, guids); + if (remoteSystems) + { + unsigned short i; + for (i=0; i < *numberOfSystems && i < addresses.Size(); i++) + remoteSystems[i]=addresses[i]; + *numberOfSystems=i; + } + else + { + *numberOfSystems=(unsigned short) addresses.Size(); + } + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::GetNextSendReceipt(void) +{ + sendReceiptSerialMutex.Lock(); + uint32_t retVal = sendReceiptSerial; + sendReceiptSerialMutex.Unlock(); + return retVal; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::IncrementNextSendReceipt(void) +{ + sendReceiptSerialMutex.Lock(); + uint32_t returned = sendReceiptSerial; + if (++sendReceiptSerial==0) + sendReceiptSerial=1; + sendReceiptSerialMutex.Unlock(); + return returned; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sends a block of data to the specified system that you are connected to. +// This function only works while the client is connected (Use the Connect function). +// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM +// +// Parameters: +// data: The block of data to send +// length: The size in bytes of the data to send +// bitStream: The bitstream to send +// priority: What priority level to send on. +// reliability: How reliability to send this data +// orderingChannel: When using ordered or sequenced packets, what channel to order these on. +// - Packets are only ordered relative to other packets on the same stream +// systemAddress: Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none +// broadcast: True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. +// Returns: +// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// A peer mid-session-handshake has not been reported to the application, so the application must not be +// able to address it. Checked here rather than in SendImmediate because the handshake's own messages go +// through SendImmediate directly and must still reach that peer. +bool RakPeer::IsExchangingSessionData( const AddressOrGUID systemIdentifier ) +{ + if (remoteSystemList==0) + return false; + + int index; + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); + else + index = GetIndexFromGuid(systemIdentifier.rakNetGuid); + + return index!=-1 && remoteSystemList[index].isActive && + remoteSystemList[index].connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::Send( const char *data, const int length, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) +{ +#ifdef _DEBUG + RakAssert( data && length > 0 ); +#endif + RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); + RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); + RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); + + if ( data == 0 || length < 0 ) + return 0; + + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + if ( broadcast == false && systemIdentifier.IsUndefined()) + return 0; + + if ( broadcast == false && IsExchangingSessionData(systemIdentifier) ) + return 0; + + uint32_t usedSendReceipt; + if (forceReceiptNumber!=0) + usedSendReceipt=forceReceiptNumber; + else + usedSendReceipt=IncrementNextSendReceipt(); + + if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) + { + SendLoopback(data,length); + + if (reliability>=MafiaNet::Reliability::UnreliableWithAckReceipt) + { + char buff[5]; + buff[0]=ID_SND_RECEIPT_ACKED; + sendReceiptSerialMutex.Lock(); + memcpy(buff+1, &sendReceiptSerial, 4); + sendReceiptSerialMutex.Unlock(); + SendLoopback( buff, 5 ); + } + + return usedSendReceipt; + } + + SendBuffered(data, length*8, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); + + return usedSendReceipt; +} + +void RakPeer::SendLoopback( const char *data, const int length ) +{ + if ( data == 0 || length < 0 ) + return; + + Packet *packet = AllocPacket(length, _FILE_AND_LINE_); + memcpy(packet->data, data, length); + packet->systemAddress = GetLoopbackAddress(); + packet->guid=myGuid; + PushBackPacket(packet, false); +} + +uint32_t RakPeer::Send( const MafiaNet::BitStream * bitStream, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) +{ +#ifdef _DEBUG + RakAssert( bitStream->GetNumberOfBytesUsed() > 0 ); +#endif + + RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); + RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); + RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); + + if ( bitStream->GetNumberOfBytesUsed() == 0 ) + return 0; + + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + if ( broadcast == false && systemIdentifier.IsUndefined() ) + return 0; + + if ( broadcast == false && IsExchangingSessionData(systemIdentifier) ) + return 0; + + uint32_t usedSendReceipt; + if (forceReceiptNumber!=0) + usedSendReceipt=forceReceiptNumber; + else + usedSendReceipt=IncrementNextSendReceipt(); + + if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) + { + SendLoopback((const char*) bitStream->GetData(),bitStream->GetNumberOfBytesUsed()); + if (reliability>=MafiaNet::Reliability::UnreliableWithAckReceipt) + { + char buff[5]; + buff[0]=ID_SND_RECEIPT_ACKED; + sendReceiptSerialMutex.Lock(); + memcpy(buff+1, &sendReceiptSerial,4); + sendReceiptSerialMutex.Unlock(); + SendLoopback( buff, 5 ); + } + return usedSendReceipt; + } + + // Sends need to be buffered and processed in the update thread because the systemAddress associated with the reliability layer can change, + // from that thread, resulting in a send to the wrong player! While I could mutex the systemAddress, that is much slower than doing this + SendBuffered((const char*)bitStream->GetData(), bitStream->GetNumberOfBitsUsed(), priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); + + + return usedSendReceipt; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Sends multiple blocks of data, concatenating them automatically. +// +// This is equivalent to: +// MafiaNet::BitStream bs; +// bs.WriteAlignedBytes(block1, blockLength1); +// bs.WriteAlignedBytes(block2, blockLength2); +// bs.WriteAlignedBytes(block3, blockLength3); +// Send(&bs, ...) +// +// This function only works while connected +// \param[in] data An array of pointers to blocks of data +// \param[in] lengths An array of integers indicating the length of each block of data +// \param[in] numParameters Length of the arrays data and lengths +// \param[in] priority What priority level to send on. See packet_priority.h +// \param[in] reliability How reliability to send this data. See packet_priority.h +// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream +// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none +// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. +// \return False if we are not connected to the specified recipient. True otherwise +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::SendList( const char **data, const int *lengths, const int numParameters, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) +{ +#ifdef _DEBUG + RakAssert( data ); +#endif + + if ( data == 0 || lengths == 0 ) + return 0; + + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + if (numParameters==0) + return 0; + + if (lengths==0) + return 0; + + if ( broadcast == false && systemIdentifier.IsUndefined() ) + return 0; + + if ( broadcast == false && IsExchangingSessionData(systemIdentifier) ) + return 0; + + uint32_t usedSendReceipt; + if (forceReceiptNumber!=0) + usedSendReceipt=forceReceiptNumber; + else + usedSendReceipt=IncrementNextSendReceipt(); + + SendBufferedList(data, lengths, numParameters, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); + + return usedSendReceipt; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Gets a packet from the incoming packet queue. Use DeallocatePacket to deallocate the packet after you are done with it. +// Check the Packet struct at the top of CoreNetworkStructures.h for the format of the struct +// +// Returns: +// 0 if no packets are waiting to be handled, otherwise an allocated packet +// If the client is not active this will also return 0, as all waiting packets are flushed when the client is Disconnected +// This also updates all memory blocks associated with synchronized memory and distributed objects +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +Packet* RakPeer::Receive( void ) +{ + if ( !( IsActive() ) ) + return 0; + + MafiaNet::Packet *packet; +// Packet **threadPacket; + PluginReceiveResult pluginResult; + + int offset; + unsigned int i; + + // User should call RunUpdateCycle and RunRecvFromOnce to do this commented code + /* +#if RAKPEER_SINGLE_THREADED==1 + RakPeer::RecvFromStruct *recvFromStruct; + for (i=0; i < socketList.Size(); i++) + { + for(;;) + { + recvFromStruct=bufferedPackets.Allocate( _FILE_AND_LINE_ ); + recvFromStruct->s=socketList[i]->s; + recvFromStruct->extraSocketOptions=socketList[i]->extraSocketOptions; + SocketLayer::RecvFromBlocking( + recvFromStruct->s, this, recvFromStruct->extraSocketOptions, recvFromStruct->data, &recvFromStruct->bytesRead, &recvFromStruct->systemAddress, &recvFromStruct->timeRead); + if (recvFromStruct->bytesRead<=0) + { + bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); + break; + } + else + { + RakAssert(recvFromStruct->systemAddress.GetPort()); + bufferedPackets.Push(recvFromStruct); + } + } + } + + BitStream updateBitStream( MAXIMUM_MTU_SIZE +#if LIBCAT_SECURITY==1 + + cat::AuthenticatedEncryption::OVERHEAD_BYTES +#endif + ); + RunUpdateCycle(0, 0, updateBitStream); +#endif + */ + + for (i=0; i < pluginListTS.Size(); i++) + { + pluginListTS[i]->Update(); + } + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginListNTS[i]->Update(); + } + + do + { + packetReturnMutex.Lock(); + if (packetReturnQueue.IsEmpty()) + packet=0; + else + packet = packetReturnQueue.Pop(); + packetReturnMutex.Unlock(); + if (packet==0) + return 0; + +// unsigned char msgId; + if ( ( packet->length >= sizeof(unsigned char) + sizeof(MafiaNet::Time ) ) && + ( (unsigned char) packet->data[ 0 ] == ID_TIMESTAMP ) ) + { + offset = sizeof(unsigned char); + ShiftIncomingTimestamp( packet->data + offset, packet->systemAddress ); +// msgId=packet->data[sizeof(unsigned char) + sizeof( MafiaNet::Time )]; + } +// else + // msgId=packet->data[0]; + + // Some locally generated packets need to be processed by plugins, for example ID_FCM2_NEW_HOST + // The plugin itself should intercept these messages generated remotely +// if (packet->wasGeneratedLocally) +// return packet; + + + // A callback may shut the peer down. The plugins have been reset by then and the packet + // belongs to a closed session, so it goes no further and Receive returns nothing. + const unsigned int generation = shutdownGeneration; + + CallPluginCallbacks(pluginListTS, packet); + CallPluginCallbacks(pluginListNTS, packet); + if (shutdownGeneration != generation) + { + DeallocatePacket( packet ); + return 0; + } + + for (i=0; i < pluginListTS.Size(); i++) + { + pluginResult=pluginListTS[i]->OnReceive(packet); + if (shutdownGeneration != generation) + { + if (pluginResult!=RR_STOP_PROCESSING) + DeallocatePacket( packet ); + return 0; + } + if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) + { + DeallocatePacket( packet ); + packet=0; // Will do the loop again and get another packet + break; // break out of the enclosing for + } + else if (pluginResult==RR_STOP_PROCESSING) + { + packet=0; + break; + } + } + + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginResult=pluginListNTS[i]->OnReceive(packet); + if (shutdownGeneration != generation) + { + if (pluginResult!=RR_STOP_PROCESSING) + DeallocatePacket( packet ); + return 0; + } + if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) + { + DeallocatePacket( packet ); + packet=0; // Will do the loop again and get another packet + break; // break out of the enclosing for + } + else if (pluginResult==RR_STOP_PROCESSING) + { + packet=0; + break; + } + } + + } while(packet==0); + +#ifdef _DEBUG + RakAssert( packet->data ); +#endif + + return packet; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Call this to deallocate a packet returned by Receive +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DeallocatePacket( Packet *packet ) +{ + if ( packet == 0 ) + return; + + if (packet->deleteData) + { + rakFree_Ex(packet->data, _FILE_AND_LINE_ ); + packet->~Packet(); + packetAllocationPoolMutex.Lock(); + packetAllocationPool.Release(packet,_FILE_AND_LINE_); + --packetsOutstanding; + packetAllocationPoolMutex.Unlock(); + } + else + { + rakFree_Ex(packet, _FILE_AND_LINE_ ); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Return the total number of connections we are allowed +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetMaximumNumberOfPeers( void ) const +{ + return maximumNumberOfPeers; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). +// +// Parameters: +// target: Which connection to close +// sendDisconnectionNotification: True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. +// channel: If blockDuration > 0, the disconnect packet will be sent on this channel +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority, const MafiaNet::BitStream *reasonData ) +{ + /* + // This only be called from the user thread, for the user shutting down. + // From the network thread, this should occur because of ID_DISCONNECTION_NOTIFICATION and ID_CONNECTION_LOST + unsigned j; + for (j=0; j < messageHandlerList.Size(); j++) + { + messageHandlerList[j]->OnClosedConnection( + target.systemAddress==UNASSIGNED_SYSTEM_ADDRESS ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress, + target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid, + LCR_CLOSED_BY_USER); + } + */ + + const SystemAddress address = (target.systemAddress == UNASSIGNED_SYSTEM_ADDRESS) ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress; + int remoteSystemListIndex = GetIndexFromSystemAddress(address); + + // Resolve the socket to close on WITHOUT assuming a valid slot index. + // GetIndexFromSystemAddress returns -1 when the target isn't in the list; never + // coerce that to 0 — reading remoteSystemList[0] would crash if the list is + // unallocated, or target an unrelated peer's slot. When the index is valid use + // that slot's socket (it may itself be null during rapid connect/disconnect + // churn). Either way, fall back to the primary socket — the same pattern used by + // the BCS_CLOSE_CONNECTION path below. With no socket at all there is nothing to + // close, so bail out. + RakNetSocket2 *closeSocket = (remoteSystemListIndex != -1) ? remoteSystemList[remoteSystemListIndex].rakNetSocket : nullptr; + if (closeSocket == nullptr && socketList.Size() > 0) + closeSocket = socketList[0]; + if (closeSocket == nullptr) + return; + CloseConnectionInternal2(target, sendDisconnectionNotification, false, orderingChannel, disconnectionNotificationPriority, *closeSocket, reasonData); + + // 12/14/09 Return ID_CONNECTION_LOST when calling CloseConnection with sendDisconnectionNotification==false, elsewise it is never returned + if (sendDisconnectionNotification==false && GetConnectionState(target)==IS_CONNECTED) + { + Packet *packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection + packet->guid = target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid; + packet->systemAddress = address; + packet->systemAddress.systemIndex = static_cast(remoteSystemListIndex == -1 ? 0 : remoteSystemListIndex); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + packet->wasGeneratedLocally=true; // else processed twice + AddPacketToProducer(packet); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Cancel a pending connection attempt +// If we are already connected, the connection stays open +// \param[in] target Which system to cancel +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::CancelConnectionAttempt( const SystemAddress target ) +{ + unsigned int i; + + // Cancel pending connection attempt, if there is one + i=0; + requestedConnectionQueueMutex.Lock(); + while (i < requestedConnectionQueue.Size()) + { + if (requestedConnectionQueue[i]->systemAddress==target) + { +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Deleting requestedConnectionQueue %i client_handshake %x\n", i, requestedConnectionQueue[ i ]->client_handshake); + MafiaNet::OP_DELETE(requestedConnectionQueue[i]->client_handshake, _FILE_AND_LINE_ ); +#endif + MafiaNet::OP_DELETE(requestedConnectionQueue[i], _FILE_AND_LINE_ ); + requestedConnectionQueue.RemoveAtIndex(i); + break; + } + else + i++; + } + requestedConnectionQueueMutex.Unlock(); + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +ConnectionState RakPeer::GetConnectionState(const AddressOrGUID systemIdentifier) +{ + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + unsigned int i=0; + requestedConnectionQueueMutex.Lock(); + for (; i < requestedConnectionQueue.Size(); i++) + { + if (requestedConnectionQueue[i]->systemAddress==systemIdentifier.systemAddress) + { + requestedConnectionQueueMutex.Unlock(); + return IS_PENDING; + } + } + requestedConnectionQueueMutex.Unlock(); + } + + int index; + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); + } + else + { + index = GetIndexFromGuid(systemIdentifier.rakNetGuid); + } + + if (index==-1) + return IS_NOT_CONNECTED; + + if (remoteSystemList[index].isActive==false) + return IS_DISCONNECTED; + + switch (remoteSystemList[index].connectMode) + { + case RemoteSystemStruct::DISCONNECT_ASAP: + return IS_DISCONNECTING; + case RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY: + return IS_SILENTLY_DISCONNECTING; + case RemoteSystemStruct::DISCONNECT_ON_NO_ACK: + return IS_DISCONNECTING; + case RemoteSystemStruct::REQUESTED_CONNECTION: + return IS_CONNECTING; + case RemoteSystemStruct::HANDLING_CONNECTION_REQUEST: + return IS_CONNECTING; + case RemoteSystemStruct::UNVERIFIED_SENDER: + return IS_CONNECTING; + case RemoteSystemStruct::EXCHANGING_SESSION_DATA: + // Transport is up but the session handshake has not completed, so the application has not been + // told this connection exists yet. + return IS_CONNECTING; + case RemoteSystemStruct::CONNECTED: + return IS_CONNECTED; + default: + return IS_NOT_CONNECTED; + } +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Given a systemAddress, returns an index from 0 to the maximum number of players allowed - 1. +// +// Parameters +// systemAddress - The systemAddress to search for +// +// Returns +// An integer from 0 to the maximum number of peers -1, or -1 if that player is not found +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress ) const +{ + return GetIndexFromSystemAddress(systemAddress, false); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// This function is only useful for looping through all players. +// +// Parameters +// index - an integer between 0 and the maximum number of players allowed - 1. +// +// Returns +// A valid systemAddress or UNASSIGNED_SYSTEM_ADDRESS if no such player at that index +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetSystemAddressFromIndex( unsigned int index ) +{ + // remoteSystemList in user thread + //if ( index >= 0 && index < remoteSystemListSize ) + if ( index < maximumNumberOfPeers ) + if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail + return remoteSystemList[ index ].systemAddress; + + return UNASSIGNED_SYSTEM_ADDRESS; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Same as GetSystemAddressFromIndex but returns RakNetGUID +// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. +// \return The RakNetGUID +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakNetGUID RakPeer::GetGUIDFromIndex( unsigned int index ) +{ + // remoteSystemList in user thread + //if ( index >= 0 && index < remoteSystemListSize ) + if ( index < maximumNumberOfPeers ) + if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail + return remoteSystemList[ index ].guid; + + return UNASSIGNED_RAKNET_GUID; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient +// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system +// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the list will match the size of the \a guids list. +// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const +{ + addresses.Clear(false, _FILE_AND_LINE_); + guids.Clear(false, _FILE_AND_LINE_); + + if ( remoteSystemList == 0 || endThreads == true ) + return; + + unsigned int i; + for (i=0; i < activeSystemListSize; i++) + { + if ((activeSystemList[i])->isActive && + (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED) + { + addresses.Push((activeSystemList[i])->systemAddress, _FILE_AND_LINE_ ); + guids.Push((activeSystemList[i])->guid, _FILE_AND_LINE_ ); + } + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Bans an IP from connecting. Banned IPs persist between connections. +// +// Parameters +// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban +// All IP addresses starting with 128.0.0 +// milliseconds - how many ms for a temporary ban. Use 0 for a permanent ban +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AddToBanList( const char *IP, MafiaNet::TimeMS milliseconds ) +{ + unsigned index; + MafiaNet::TimeMS time = MafiaNet::GetTimeMS(); + + if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) + return ; + + // If this guy is already in the ban list, do nothing + index = 0; + + banListMutex.Lock(); + + for ( ; index < banList.Size(); index++ ) + { + if ( strcmp( IP, banList[ index ]->IP ) == 0 ) + { + // Already in the ban list. Just update the time + if (milliseconds==0) + banList[ index ]->timeout=0; // Infinite + else + banList[ index ]->timeout=time+milliseconds; + banListMutex.Unlock(); + return; + } + } + + banListMutex.Unlock(); + + BanStruct *banStruct = MafiaNet::OP_NEW( _FILE_AND_LINE_ ); + banStruct->IP = (char*) rakMalloc_Ex( 16, _FILE_AND_LINE_ ); + if (milliseconds==0) + banStruct->timeout=0; // Infinite + else + banStruct->timeout=time+milliseconds; + strcpy_s( banStruct->IP, 16, IP ); + banListMutex.Lock(); + banList.Insert( banStruct, _FILE_AND_LINE_ ); + banListMutex.Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Allows a previously banned IP to connect. +// +// Parameters +// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban +// All IP addresses starting with 128.0.0 +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RemoveFromBanList( const char *IP ) +{ + unsigned index; + BanStruct *temp; + + if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) + return ; + + index = 0; + temp=0; + + banListMutex.Lock(); + + for ( ; index < banList.Size(); index++ ) + { + if ( strcmp( IP, banList[ index ]->IP ) == 0 ) + { + temp = banList[ index ]; + banList[ index ] = banList[ banList.Size() - 1 ]; + banList.RemoveAtIndex( banList.Size() - 1 ); + break; + } + } + + banListMutex.Unlock(); + + if (temp) + { + rakFree_Ex(temp->IP, _FILE_AND_LINE_ ); + MafiaNet::OP_DELETE(temp, _FILE_AND_LINE_); + } + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Allows all previously banned IPs to connect. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearBanList( void ) +{ + unsigned index; + index = 0; + banListMutex.Lock(); + + for ( ; index < banList.Size(); index++ ) + { + rakFree_Ex(banList[ index ]->IP, _FILE_AND_LINE_ ); + MafiaNet::OP_DELETE(banList[ index ], _FILE_AND_LINE_); + } + + banList.Clear(false, _FILE_AND_LINE_); + + banListMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetLimitIPConnectionFrequency(bool b) +{ + limitConnectionFrequencyFromTheSameIP=b; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Determines if a particular IP is banned. +// +// Parameters +// IP - Complete dotted IP address +// +// Returns +// True if IP matches any IPs in the ban list, accounting for any wildcards. +// False otherwise. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsBanned( const char *IP ) +{ + unsigned banListIndex, characterIndex; + MafiaNet::TimeMS time; + BanStruct *temp; + + if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) + return false; + + banListIndex = 0; + + if ( banList.Size() == 0 ) + return false; // Skip the mutex if possible + + time = MafiaNet::GetTimeMS(); + + banListMutex.Lock(); + + while ( banListIndex < banList.Size() ) + { + if (banList[ banListIndex ]->timeout>0 && banList[ banListIndex ]->timeoutIP, _FILE_AND_LINE_ ); + MafiaNet::OP_DELETE(temp, _FILE_AND_LINE_); + } + else + { + characterIndex = 0; + + for(;;) + { + if ( banList[ banListIndex ]->IP[ characterIndex ] == IP[ characterIndex ] ) + { + // Equal characters + + if ( IP[ characterIndex ] == 0 ) + { + banListMutex.Unlock(); + // End of the string and the strings match + + return true; + } + + characterIndex++; + } + + else + { + if ( banList[ banListIndex ]->IP[ characterIndex ] == 0 || IP[ characterIndex ] == 0 ) + { + // End of one of the strings + break; + } + + // Characters do not match + if ( banList[ banListIndex ]->IP[ characterIndex ] == '*' ) + { + banListMutex.Unlock(); + + // Domain is banned. + return true; + } + + // Characters do not match and it is not a * + break; + } + } + + banListIndex++; + } + } + + banListMutex.Unlock(); + + // No match found. + return false; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Send a ping to the specified connected system. +// +// Parameters: +// target - who to ping +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::Ping( const SystemAddress target ) +{ + PingInternal(target, false, MafiaNet::Reliability::Unreliable); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Send a ping to the specified unconnected system. +// The remote system, if it is Initialized, will respond with ID_UNCONNECTED_PONG. +// The final ping time will be encoded in the following sizeof(MafiaNet::TimeMS) bytes. (Default is 4 bytes - See __GET_TIME_64BIT in types.h +// +// Parameters: +// host: Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. +// remotePort: Which port to connect to on the remote machine. +// onlyReplyOnAcceptingConnections: Only request a reply if the remote system has open connections +// connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex ) +{ + if ( host == 0 ) + return false; + + // If this assert hits then Startup wasn't called or the call failed. + RakAssert(connectionSocketIndex < socketList.Size()); + +// if ( IsActive() == false ) +// return; + + MafiaNet::BitStream bitStream( sizeof(unsigned char) + sizeof(MafiaNet::Time) ); + if ( onlyReplyOnAcceptingConnections ) + bitStream.Write((MessageID)ID_UNCONNECTED_PING_OPEN_CONNECTIONS); + else + bitStream.Write((MessageID)ID_UNCONNECTED_PING); + + bitStream.Write(MafiaNet::GetTime()); + + bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + + bitStream.Write(GetMyGUID()); + + // No timestamp for 255.255.255.255 + unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + /* + + SystemAddress systemAddress; + systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); + systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); + + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( socketList[realIndex], (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + */ + + RNS2_SendParameters bsp; + bsp.data = (char*) bitStream.GetData() ; + bsp.length = bitStream.GetNumberOfBytesUsed(); + bsp.systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); + if (bsp.systemAddress==UNASSIGNED_SYSTEM_ADDRESS) + return false; + bsp.systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), bsp.systemAddress); + socketList[realIndex]->Send(&bsp, _FILE_AND_LINE_); + + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the average of all ping times read for a specified target +// +// Parameters: +// target - whose time to read +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetAveragePing( const AddressOrGUID systemIdentifier ) +{ + int sum, quantity; + RemoteSystemStruct *remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); + + if ( remoteSystem == 0 ) + return -1; + + for ( sum = 0, quantity = 0; quantity < PING_TIMES_ARRAY_SIZE; quantity++ ) + { + if ( remoteSystem->pingAndClockDifferential[ quantity ].pingTime == 65535 ) + break; + else + sum += remoteSystem->pingAndClockDifferential[ quantity ].pingTime; + } + + if ( quantity > 0 ) + return sum / quantity; + else + return -1; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the last ping time read for the specific player or -1 if none read yet +// +// Parameters: +// target - whose time to read +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetLastPing( const AddressOrGUID systemIdentifier ) const +{ + RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); + + if ( remoteSystem == 0 ) + return -1; + +// return (int)(remoteSystem->reliabilityLayer.GetAckPing()/(MafiaNet::TimeUS)1000); + + if ( remoteSystem->pingAndClockDifferentialWriteIndex == 0 ) + return remoteSystem->pingAndClockDifferential[ PING_TIMES_ARRAY_SIZE - 1 ].pingTime; + else + return remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex - 1 ].pingTime; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the lowest ping time read or -1 if none read yet +// +// Parameters: +// target - whose time to read +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetLowestPing( const AddressOrGUID systemIdentifier ) const +{ + RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); + + if ( remoteSystem == 0 ) + return -1; + + return remoteSystem->lowestPing; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Ping the remote systems every so often. This is off by default +// This will work anytime +// +// Parameters: +// doPing - True to start occasional pings. False to stop them. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetOccasionalPing( bool doPing ) +{ + occasionalPing = doPing; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +/// Return the clock difference between your system and the specified system +/// Subtract the time from a time returned by the remote system to get that time relative to your own system +/// Returns 0 if the system is unknown +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +MafiaNet::Time RakPeer::GetClockDifferential( const AddressOrGUID systemIdentifier ) +{ + RemoteSystemStruct *remoteSystem = GetRemoteSystem(systemIdentifier, false, false); + if (remoteSystem == 0) + return 0; + return GetClockDifferentialInt(remoteSystem); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +MafiaNet::Time RakPeer::GetClockDifferentialInt(RemoteSystemStruct *remoteSystem) const +{ + int counter, lowestPingSoFar; + MafiaNet::Time clockDifferential; + + lowestPingSoFar = 65535; + + clockDifferential = 0; + + for ( counter = 0; counter < PING_TIMES_ARRAY_SIZE; counter++ ) + { + if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime == 65535 ) + break; + + if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime < lowestPingSoFar ) + { + clockDifferential = remoteSystem->pingAndClockDifferential[ counter ].clockDifferential; + lowestPingSoFar = remoteSystem->pingAndClockDifferential[ counter ].pingTime; + } + } + + return clockDifferential; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Length should be under 400 bytes, as a security measure against flood attacks +// Sets the data to send with an (LAN server discovery) /(offline ping) response +// See the Ping sample project for how this is used. +// data: a block of data to store, or 0 for none +// length: The length of data in bytes, or 0 for none +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetOfflinePingResponse( const char *data, const unsigned int length ) +{ + RakAssert(length < 400); + + rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); + offlinePingResponse.Reset(); + + if ( data && length > 0 ) + offlinePingResponse.Write( data, length ); + + rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns pointers to a copy of the data passed to SetOfflinePingResponse +// \param[out] data A pointer to a copy of the data passed to \a SetOfflinePingResponse() +// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() +// \sa SetOfflinePingResponse +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetOfflinePingResponse( char **data, unsigned int *length ) +{ + rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); + *data = (char*) offlinePingResponse.GetData(); + *length = (int) offlinePingResponse.GetNumberOfBytesUsed(); + rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Sets the payload this peer offers during the session handshake. Opaque to MafiaNet. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetSessionConfig( const char *data, unsigned int length ) +{ + RakAssert(length <= MAXIMUM_SESSION_CONFIG_SIZE); + if (length > MAXIMUM_SESSION_CONFIG_SIZE) + length = MAXIMUM_SESSION_CONFIG_SIZE; + + rakPeerMutexes[ sessionConfig_Mutex ].Lock(); + sessionConfig.Reset(); + + if ( data && length > 0 ) + sessionConfig.Write( data, length ); + + rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetSessionConfig( char **data, unsigned int *length ) +{ + rakPeerMutexes[ sessionConfig_Mutex ].Lock(); + *data = (char*) sessionConfig.GetData(); + *length = (unsigned int) sessionConfig.GetNumberOfBytesUsed(); + rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +const char *RakPeer::GetRemoteSessionConfig( const AddressOrGUID systemIdentifier, unsigned int *length ) +{ + if (length) + *length = 0; + + int index; + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); + else + index = GetIndexFromGuid(systemIdentifier.rakNetGuid); + + if (index==-1 || remoteSystemList==0 || remoteSystemList[index].isActive==false) + return 0; + + if (length) + *length = remoteSystemList[index].remoteSessionConfigLength; + return (const char*) remoteSystemList[index].remoteSessionConfigData; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetSessionConfigInteractive( bool interactive ) +{ + sessionConfigInteractive = interactive; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AcceptSession( const AddressOrGUID systemIdentifier, const char *data, unsigned int length ) +{ + RakAssert(length <= MAXIMUM_SESSION_CONFIG_SIZE); + if (length > MAXIMUM_SESSION_CONFIG_SIZE) + length = MAXIMUM_SESSION_CONFIG_SIZE; + QueueSessionDecision(systemIdentifier, true, data, length); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RejectSession( const AddressOrGUID systemIdentifier, const char *reason ) +{ + unsigned int reasonLength = (reason && reason[0]) ? (unsigned int) strlen(reason) : 0; + // Bounded like every other payload that crosses the handshake. A reason is meant to be a short + // string, so an oversized one is a caller mistake rather than something to transmit. + RakAssert(reasonLength <= MAXIMUM_SESSION_CONFIG_SIZE); + if (reasonLength > MAXIMUM_SESSION_CONFIG_SIZE) + reasonLength = MAXIMUM_SESSION_CONFIG_SIZE; + QueueSessionDecision(systemIdentifier, false, reason, reasonLength); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetMaximumPendingSessions( unsigned short total, unsigned short perAddress ) +{ + maximumPendingSessions = total; + maximumPendingSessionsPerAddress = total != 0 ? perAddress : 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetSessionTimeout( MafiaNet::TimeMS timeMS ) +{ + sessionTimeout = timeMS; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendSessionStatus( const AddressOrGUID systemIdentifier, const char *data, unsigned int length ) +{ + RakAssert(length <= MAXIMUM_SESSION_CONFIG_SIZE); + if (length > MAXIMUM_SESSION_CONFIG_SIZE) + length = MAXIMUM_SESSION_CONFIG_SIZE; + QueueSessionCommand(systemIdentifier, BufferedCommandStruct::BCS_SESSION_STATUS, data, length); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Hand an application's accept/reject over to the network thread. Applying it here would send on the +// connection and mutate connectMode from the user thread, which the rest of RakPeer never does. +void RakPeer::QueueSessionDecision( const AddressOrGUID systemIdentifier, bool accept, const char *data, unsigned int length ) +{ + QueueSessionCommand(systemIdentifier, accept ? BufferedCommandStruct::BCS_SESSION_ACCEPT : BufferedCommandStruct::BCS_SESSION_REJECT, data, length); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::QueueSessionCommand( const AddressOrGUID systemIdentifier, int command, const char *data, unsigned int length ) +{ + BufferedCommandStruct *bcs; + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->command = (decltype(bcs->command)) command; + bcs->systemIdentifier = systemIdentifier; + bcs->numberOfBitsToSend = BYTES_TO_BITS(length); + bcs->broadcast = false; + bcs->connectionMode = RemoteSystemStruct::NO_ACTION; + bcs->orderingChannel = 0; + bcs->priority = MafiaNet::Priority::Immediate; + bcs->reliability = MafiaNet::Reliability::ReliableOrdered; + bcs->data = 0; + if (data && length > 0) + { + bcs->data = (char*) rakMalloc_Ex(length, _FILE_AND_LINE_); + if (bcs->data) + memcpy(bcs->data, data, length); + else + bcs->numberOfBitsToSend = 0; + } + bufferedCommands.Push(bcs); + quitAndDataEvents.SetEvent(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Return the unique SystemAddress that represents you on the the network +// Note that unlike in previous versions, this is a struct and is not sequential +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetInternalID( const SystemAddress systemAddress, const int index ) const +{ + if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) + { + return ipList[index]; + } + else + { + +// SystemAddress returnValue; + RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, false, true ); + if (remoteSystem==0) + return UNASSIGNED_SYSTEM_ADDRESS; + + return remoteSystem->theirInternalSystemAddress[index]; + /* + sockaddr_in sa; + socklen_t len = sizeof(sa); + if (getsockname__(connectionSockets[remoteSystem->connectionSocketIndex], (sockaddr*)&sa, &len)!=0) + return UNASSIGNED_SYSTEM_ADDRESS; + returnValue.port=ntohs(sa.sin_port); + returnValue.binaryAddress=sa.sin_addr.s_addr; + return returnValue; +*/ + + + + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +/// \brief Sets your internal IP address, for platforms that do not support reading it, or to override a value +/// \param[in] systemAddress. The address to set. Use SystemAddress::FromString() if you want to use a dotted string +/// \param[in] index When you have multiple internal IDs, which index to set? +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetInternalID(SystemAddress systemAddress, int index) +{ + RakAssert(index >=0 && index < MAXIMUM_NUMBER_OF_INTERNAL_IDS); + ipList[index]=systemAddress; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Return the unique address identifier that represents you on the the network and is based on your external +// IP / port (the IP / port the specified player uses to communicate with you) +// Note that unlike in previous versions, this is a struct and is not sequential +// +// Parameters: +// target: Which remote system you are referring to for your external ID +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetExternalID( const SystemAddress target ) const +{ + unsigned i; + SystemAddress inactiveExternalId; + + inactiveExternalId=UNASSIGNED_SYSTEM_ADDRESS; + + if (target==UNASSIGNED_SYSTEM_ADDRESS) + return firstExternalID; + + // First check for active connection with this systemAddress + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].systemAddress == target ) + { + if ( remoteSystemList[ i ].isActive ) + return remoteSystemList[ i ].myExternalSystemAddress; + else if (remoteSystemList[ i ].myExternalSystemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + inactiveExternalId=remoteSystemList[ i ].myExternalSystemAddress; + } + } + + return inactiveExternalId; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +const RakNetGUID RakPeer::GetMyGUID(void) const +{ + return myGuid; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetMyBoundAddress(const int socketIndex) +{ + DataStructures::List sockets; + GetSockets( sockets ); + if (sockets.Size()>0) + return sockets[socketIndex]->GetBoundAddress(); + else + return UNASSIGNED_SYSTEM_ADDRESS; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +const RakNetGUID& RakPeer::GetGuidFromSystemAddress( const SystemAddress input ) const +{ + if (input==UNASSIGNED_SYSTEM_ADDRESS) + return myGuid; + + if (input.systemIndex!=(SystemIndex)-1 && input.systemIndexreliabilityLayer.SetTimeoutTime(timeMS); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +MafiaNet::TimeMS RakPeer::GetTimeoutTime( const SystemAddress target ) +{ + if (target==UNASSIGNED_SYSTEM_ADDRESS) + { + return defaultTimeoutTime; + } + else + { + RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( target, false, true ); + + if ( remoteSystem != 0 ) + return remoteSystem->reliabilityLayer.GetTimeoutTime(); + } + return defaultTimeoutTime; +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the current MTU size +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetMTUSize( const SystemAddress target ) const +{ + if (target!=UNASSIGNED_SYSTEM_ADDRESS) + { + RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(target, false, true); + if (rss) + return rss->MTUSize; + } + return defaultMTUSize; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the number of IP addresses we have +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetNumberOfAddresses( void ) +{ + if (IsActive() == false) { + FillIPList(); + } + + unsigned int i = 0; + while (i < MAXIMUM_NUMBER_OF_INTERNAL_IDS && ipList[i] != UNASSIGNED_SYSTEM_ADDRESS) + i++; // count assigned entries until the first unassigned one (end of list) + + return i; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns an IP address at index 0 to GetNumberOfAddresses-1 +// \param[in] index index into the list of IP addresses +// \return The local IP address at this index +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +const char* RakPeer::GetLocalIP( unsigned int index ) +{ + if (IsActive()==false) + { + // Fill out ipList structure + + FillIPList(); + + } + + + static char str[128]; + ipList[index].ToString(false,str,static_cast(128)); + return str; + + + + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Is this a local IP? +// \param[in] An IP address to check +// \return True if this is one of the IP addresses returned by GetLocalIP +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsLocalIP( const char *ip ) +{ + if (ip==0 || ip[0]==0) + return false; + + // #med - this should also check for "::1" here in IPv6 mode + if (strcmp(ip, "127.0.0.1")==0 || strcmp(ip, "localhost")==0) + return true; + + int num = GetNumberOfAddresses(); + int i; + for (i=0; i < num; i++) + { + if (strcmp(ip, GetLocalIP(i))==0) + return true; + } + + + + + return false; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary +// when connection to servers with multiple IP addresses +// +// Parameters: +// allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AllowConnectionResponseIPMigration( bool allow ) +{ + allowConnectionResponseIPMigration = allow; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sends a message ID_ADVERTISE_SYSTEM to the remote unconnected system. +// This will tell the remote system our external IP outside the LAN, and can be used for NAT punch through +// +// Requires: +// The sender and recipient must already be started via a successful call to Initialize +// +// host: Either a dotted IP address or a domain name +// remotePort: Which port to connect to on the remote machine. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex ) +{ + MafiaNet::BitStream bs; + bs.Write((MessageID)ID_ADVERTISE_SYSTEM); + bs.WriteAlignedBytes((const unsigned char*) data,dataLength); + return SendOutOfBand(host, remotePort, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), connectionSocketIndex ); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. +// ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived +// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. +// Defaults to 0 (never return this notification) +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetSplitMessageProgressInterval(int interval) +{ + RakAssert(interval>=0); + splitMessageProgressInterval=interval; + for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) + remoteSystemList[ i ].reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns what was passed to SetSplitMessageProgressInterval() +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetSplitMessageProgressInterval(void) const +{ + return splitMessageProgressInterval; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Set how long to wait before giving up on sending an unreliable message +// Useful if the network is clogged up. +// Set to 0 or less to never timeout. Defaults to 0. +// timeoutMS How many ms to wait before simply not sending an unreliable message. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetUnreliableTimeout(MafiaNet::TimeMS timeoutMS) +{ + unreliableTimeout=timeoutMS; + for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) + remoteSystemList[ i ].reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Send a message to host, with the IP socket option TTL set to 3 +// This message will not reach the host, but will open the router. +// Used for NAT-Punchthrough +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex ) +{ + char fakeData[2]; + fakeData[0]=0; + fakeData[1]=1; + unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + if (socketList[realIndex]->IsBerkleySocket()) + { + RNS2_SendParameters bsp; + bsp.data = (char*) fakeData; + bsp.length = 2; + bsp.systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); + bsp.systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); + bsp.ttl=ttl; + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*)bsp.data, BYTES_TO_BITS(bsp.length), bsp.systemAddress); + socketList[realIndex]->Send(&bsp, _FILE_AND_LINE_); + } +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Attatches a Plugin interface to run code automatically on message receipt in the Receive call +// +// \param messageHandler Pointer to a plugin to attach +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AttachPlugin( PluginInterface2 *plugin ) +{ + if (IsInsideNetworkThreadReceive()) + return; + + bool isNotThreadsafe = plugin->UsesReliabilityLayer(); + if (isNotThreadsafe) + { + if (pluginListNTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) + { + plugin->SetRakPeerInterface(this); + plugin->OnAttach(); + pluginListNTS.Insert(plugin, _FILE_AND_LINE_); + } + } + else + { + if (pluginListTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) + { + plugin->SetRakPeerInterface(this); + plugin->OnAttach(); + pluginListTS.Insert(plugin, _FILE_AND_LINE_); + } + } + + pluginListNetworkThreadMutex.Lock(); + if (pluginListNetworkThread.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) + pluginListNetworkThread.Insert(plugin, _FILE_AND_LINE_); + pluginListNetworkThreadMutex.Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Detaches a Plugin interface to run code automatically on message receipt +// +// \param messageHandler Pointer to a plugin to detach +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DetachPlugin( PluginInterface2 *plugin ) +{ + if (plugin==0 || IsInsideNetworkThreadReceive()) + return; + + unsigned int index; + + bool isNotThreadsafe = plugin->UsesReliabilityLayer(); + if (isNotThreadsafe) + { + index = pluginListNTS.GetIndexOf(plugin); + if (index!=MAX_UNSIGNED_LONG) + { + // Unordered list so delete from end for speed + pluginListNTS[index]=pluginListNTS[pluginListNTS.Size()-1]; + pluginListNTS.RemoveFromEnd(); + } + } + else + { + index = pluginListTS.GetIndexOf(plugin); + if (index!=MAX_UNSIGNED_LONG) + { + // Unordered list so delete from end for speed + pluginListTS[index]=pluginListTS[pluginListTS.Size()-1]; + pluginListTS.RemoveFromEnd(); + } + } + // Removed under the lock the update thread dispatches under, so once this returns the plugin + // is not inside OnReceiveNetworkThread and will not be called again. + pluginListNetworkThreadMutex.Lock(); + index = pluginListNetworkThread.GetIndexOf(plugin); + if (index!=MAX_UNSIGNED_LONG) + { + pluginListNetworkThread[index]=pluginListNetworkThread[pluginListNetworkThread.Size()-1]; + pluginListNetworkThread.RemoveFromEnd(); + } + pluginListNetworkThreadMutex.Unlock(); + + plugin->OnDetach(); + plugin->SetRakPeerInterface(0); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Put a packet back at the end of the receive queue in case you don't want to deal with it immediately +// +// packet The packet you want to push back. +// pushAtHead True to push the packet so that the next receive call returns it. False to push it at the end of the queue (obviously pushing it at the end makes the packets out of order) +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::PushBackPacket( Packet *packet, bool pushAtHead) +{ + if (packet==0) + return; + + unsigned i; + for (i=0; i < pluginListTS.Size(); i++) + pluginListTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); + + packetReturnMutex.Lock(); + if (pushAtHead) + packetReturnQueue.PushAtHead(packet,0,_FILE_AND_LINE_); + else + packetReturnQueue.Push(packet,_FILE_AND_LINE_); + packetReturnMutex.Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress) +{ + BufferedCommandStruct *bcs; + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->data = 0; + bcs->systemIdentifier.systemAddress=systemAddress; + bcs->systemIdentifier.rakNetGuid=guid; + bcs->command=BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS; + bufferedCommands.Push(bcs); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +Packet* RakPeer::AllocatePacket(unsigned dataSize) +{ + return AllocPacket(dataSize, _FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakNetSocket2* RakPeer::GetSocket( const SystemAddress target ) +{ + // Send a query to the thread to get the socket, and return when we got it + BufferedCommandStruct *bcs; + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; + bcs->systemIdentifier=target; + bcs->data=0; + bufferedCommands.Push(bcs); + + // Block up to one second to get the socket, although it should actually take virtually no time + SocketQueryOutput *sqo; + MafiaNet::TimeMS stopWaiting = MafiaNet::GetTimeMS()+1000; + DataStructures::List output; + while (manualUpdate || MafiaNet::GetTimeMS() < stopWaiting) + { + if (manualUpdate) + RunManualUpdate(); // answers the query on this thread + else if (isMainLoopThreadActive==false) + return 0; + else + RakSleep(0); + + sqo = socketQueryOutput.Pop(); + if (sqo) + { + output=sqo->sockets; + sqo->sockets.Clear(false, _FILE_AND_LINE_); + socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); + if (output.Size()) + return output[0]; + break; + } + } + return 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetSockets( DataStructures::List &sockets ) +{ + sockets.Clear(false, _FILE_AND_LINE_); + + // Send a query to the thread to get the socket, and return when we got it + BufferedCommandStruct *bcs; + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; + bcs->systemIdentifier=UNASSIGNED_SYSTEM_ADDRESS; + bcs->data=0; + bufferedCommands.Push(bcs); + + // Block up to one second to get the socket, although it should actually take virtually no time + SocketQueryOutput *sqo; +// RakNetSocket2* output; + for(;;) + { + if (manualUpdate) + RunManualUpdate(); // answers the query on this thread + else if (isMainLoopThreadActive==false) + return; + else + RakSleep(0); + + sqo = socketQueryOutput.Pop(); + if (sqo) + { + sockets=sqo->sockets; + sqo->sockets.Clear(false, _FILE_AND_LINE_); + socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); + return; + } + } + return; +} +void RakPeer::ReleaseSockets( DataStructures::List &sockets ) +{ + sockets.Clear(false,_FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Adds simulated ping and packet loss to the outgoing data flow. +// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and maxSendBPS value on each. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance) +{ +#ifndef _DEBUG + // unused parameters + (void)packetloss; + (void)minExtraPing; + (void)extraPingVariance; +#endif + +#ifdef _DEBUG + if (remoteSystemList) + { + unsigned short i; + for (i=0; i < maximumNumberOfPeers; i++) + //for (i=0; i < remoteSystemListSize; i++) + remoteSystemList[i].reliabilityLayer.ApplyNetworkSimulator(packetloss, minExtraPing, extraPingVariance); + } + + _packetloss=packetloss; + _minExtraPing=minExtraPing; + _extraPingVariance=extraPingVariance; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void RakPeer::SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond ) +{ + maxOutgoingBPS=maxBitsPerSecond; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns if you previously called ApplyNetworkSimulator +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsNetworkSimulatorActive( void ) +{ +#ifdef _DEBUG + return _packetloss>0 || _minExtraPing>0 || _extraPingVariance>0; +#else + return false; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::WriteOutOfBandHeader(MafiaNet::BitStream *bitStream) +{ + bitStream->Write((MessageID)ID_OUT_OF_BAND_INTERNAL); + bitStream->Write(myGuid); + bitStream->WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData) +{ + userUpdateThreadPtr=_userUpdateThreadPtr; + userUpdateThreadData=_userUpdateThreadData; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetSocketFactory(RakNetSocket2Factory *factory) +{ + RakAssert(IsActive() == false); + socketFactory = factory; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetManualUpdate(bool manual) +{ + RakAssert(IsActive() == false); + manualUpdate = manual; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RunManualUpdate() +{ + RakAssert(manualUpdate); + // The same steps as one pass of UpdateNetworkLoop. + if (userUpdateThreadPtr) + userUpdateThreadPtr(this, userUpdateThreadData); + BitStream updateBitStream( MAXIMUM_MTU_SIZE +#if LIBCAT_SECURITY==1 + + cat::AuthenticatedEncryption::OVERHEAD_BYTES +#endif + ); + RunUpdateCycle(updateBitStream); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetIncomingDatagramEventHandler( bool (*_incomingDatagramEventHandler)(RNS2RecvStruct *) ) +{ + incomingDatagramEventHandler=_incomingDatagramEventHandler; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex ) +{ + if ( IsActive() == false ) + return false; + + if (host==0 || host[0]==0) + return false; + + // If this assert hits then Startup wasn't called or the call failed. + RakAssert(connectionSocketIndex < socketList.Size()); + + // This is a security measure. Don't send data longer than this value + RakAssert(dataLength <= (MAX_OFFLINE_DATA_LENGTH + sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID))); + + if (host==0) + return false; + + // 34 bytes + MafiaNet::BitStream bitStream; + WriteOutOfBandHeader(&bitStream); + + if (dataLength>0) + { + bitStream.Write(data, dataLength); + } + + unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + + /* + SystemAddress systemAddress; + systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); + systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); + + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); + + SocketLayer::SendTo( socketList[realIndex], (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + */ + + RNS2_SendParameters bsp; + bsp.data = (char*) bitStream.GetData(); + bsp.length = bitStream.GetNumberOfBytesUsed(); + bsp.systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); + bsp.systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*)bsp.data, BYTES_TO_BITS(bsp.length), bsp.systemAddress); + socketList[realIndex]->Send(&bsp, _FILE_AND_LINE_); + + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakNetStatistics * RakPeer::GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns ) +{ + static RakNetStatistics staticStatistics; + RakNetStatistics *systemStats; + if (rns==0) + systemStats=&staticStatistics; + else + systemStats=rns; + + if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) + { + bool firstWrite=false; + // Return a crude sum + for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].isActive) + { + RakNetStatistics rnsTemp; + remoteSystemList[ i ].reliabilityLayer.GetStatistics(&rnsTemp); + + if (firstWrite==false) + { + memcpy(systemStats, &rnsTemp, sizeof(RakNetStatistics)); + firstWrite=true; + } + else + (*systemStats)+=rnsTemp; + } + } + return systemStats; + } + else + { + RemoteSystemStruct * rss; + rss = GetRemoteSystemFromSystemAddress( systemAddress, false, false ); + if ( rss && endThreads==false ) + { + rss->reliabilityLayer.GetStatistics(systemStats); + return systemStats; + } + } + + return 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetStatisticsList(DataStructures::List &addresses, DataStructures::List &guids, DataStructures::List &statistics) +{ + addresses.Clear(false, _FILE_AND_LINE_); + guids.Clear(false, _FILE_AND_LINE_); + statistics.Clear(false, _FILE_AND_LINE_); + + if ( remoteSystemList == 0 || endThreads == true ) + return; + + unsigned int i; + for (i=0; i < activeSystemListSize; i++) + { + if ((activeSystemList[i])->isActive && + (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED) + { + addresses.Push((activeSystemList[i])->systemAddress, _FILE_AND_LINE_ ); + guids.Push((activeSystemList[i])->guid, _FILE_AND_LINE_ ); + RakNetStatistics rns; + (activeSystemList[i])->reliabilityLayer.GetStatistics(&rns); + statistics.Push(rns, _FILE_AND_LINE_); + } + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::GetStatistics( const unsigned int index, RakNetStatistics *rns ) +{ + if (index < maximumNumberOfPeers && remoteSystemList[ index ].isActive) + { + remoteSystemList[ index ].reliabilityLayer.GetStatistics(rns); + return true; + } + return false; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetReceiveBufferSize(void) +{ + unsigned int size; + packetReturnMutex.Lock(); + size=packetReturnQueue.Size(); + packetReturnMutex.Unlock(); + return size; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread ) const +{ + unsigned i; + + if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) + return -1; + + if (systemAddress.systemIndex!=(SystemIndex)-1 && systemAddress.systemIndex < maximumNumberOfPeers && remoteSystemList[systemAddress.systemIndex].systemAddress==systemAddress && remoteSystemList[ systemAddress.systemIndex ].isActive) + return systemAddress.systemIndex; + + if (calledFromNetworkThread) + { + return GetRemoteSystemIndex(systemAddress); + } + else + { + // remoteSystemList in user and network thread + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].systemAddress == systemAddress ) + return i; + + // If no active results found, try previously active results. + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].systemAddress == systemAddress ) + return i; + } + + return -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetIndexFromGuid( const RakNetGUID guid ) +{ + unsigned i; + + if ( guid == UNASSIGNED_RAKNET_GUID ) + return -1; + + if (guid.systemIndex!=(SystemIndex)-1 && guid.systemIndex < maximumNumberOfPeers && remoteSystemList[guid.systemIndex].guid==guid && remoteSystemList[ guid.systemIndex ].isActive) + return guid.systemIndex; + + // remoteSystemList in user and network thread + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].guid == guid ) + return i; + + // If no active results found, try previously active results. + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].guid == guid ) + return i; + + return -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +#if LIBCAT_SECURITY==1 +bool RakPeer::GenerateConnectionRequestChallenge(RequestedConnectionStruct *rcs,PublicKey *publicKey) +{ + CAT_AUDIT_PRINTF("AUDIT: In GenerateConnectionRequestChallenge()\n"); + + rcs->client_handshake = 0; + rcs->publicKeyMode = PKM_INSECURE_CONNECTION; + + if (!publicKey) return true; + + switch (publicKey->publicKeyMode) + { + default: + case PKM_INSECURE_CONNECTION: + break; + + case PKM_ACCEPT_ANY_PUBLIC_KEY: + CAT_OBJCLR(rcs->remote_public_key); + rcs->client_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); + + rcs->publicKeyMode = PKM_ACCEPT_ANY_PUBLIC_KEY; + break; + + case PKM_USE_TWO_WAY_AUTHENTICATION: + if (publicKey->myPublicKey == 0 || publicKey->myPrivateKey == 0 || + publicKey->remoteServerPublicKey == 0) + { + return false; + } + + rcs->client_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); + memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); + + if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || + !rcs->client_handshake->SetIdentity(publicKey->myPublicKey, publicKey->myPrivateKey) || + !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) + { + CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object with identity for this RequestedConnectionStruct\n"); + MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + rcs->client_handshake=0; + return false; + } + + CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object with identity for this RequestedConnectionStruct -- pre-generated challenge\n"); + + rcs->publicKeyMode = PKM_USE_TWO_WAY_AUTHENTICATION; + break; + + case PKM_USE_KNOWN_PUBLIC_KEY: + if (publicKey->remoteServerPublicKey == 0) + return false; + + rcs->client_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); + memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); + + if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || + !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) + { + CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object for this RequestedConnectionStruct\n"); + MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + rcs->client_handshake=0; + return false; + } + + CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object for this RequestedConnectionStruct -- pre-generated challenge\n"); + + rcs->publicKeyMode = PKM_USE_KNOWN_PUBLIC_KEY; + break; + } + + return true; +} +#endif +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime ) +{ + RakAssert(passwordDataLength <= 256); + RakAssert(remotePort!=0); + SystemAddress systemAddress; + if (!systemAddress.FromStringExplicitPort(host,remotePort,socketList[connectionSocketIndex]->GetBoundAddress().GetIPVersion())) + return CANNOT_RESOLVE_DOMAIN_NAME; + + // Already connected? + if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) + return ALREADY_CONNECTED_TO_ENDPOINT; + + //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); + RequestedConnectionStruct *rcs = MafiaNet::OP_NEW(_FILE_AND_LINE_); + + rcs->systemAddress=systemAddress; + rcs->nextRequestTime= MafiaNet::GetTimeMS(); + rcs->requestsMade=0; + rcs->data=0; + rcs->socket=0; + rcs->extraData=extraData; + rcs->socketIndex=connectionSocketIndex; + rcs->actionToTake=RequestedConnectionStruct::CONNECT; + rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; + rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; + // No password means passwordData is null. memcpy's source is declared nonnull, + // so a null source is undefined behaviour even with a length of 0. + if (passwordDataLength > 0) + memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); + rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; + rcs->timeoutTime=timeoutTime; + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: In SendConnectionRequest()\n"); + if (!GenerateConnectionRequestChallenge(rcs,publicKey)) + return SECURITY_INITIALIZATION_FAILED; +#else + (void) publicKey; +#endif + + // Return false if already pending, else push on queue + unsigned int i=0; + requestedConnectionQueueMutex.Lock(); + for (; i < requestedConnectionQueue.Size(); i++) + { + if (requestedConnectionQueue[i]->systemAddress==systemAddress) + { + requestedConnectionQueueMutex.Unlock(); + // Not necessary + //MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); + return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; + } + } + requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); + requestedConnectionQueueMutex.Unlock(); + + return CONNECTION_ATTEMPT_STARTED; +} +ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime, RakNetSocket2* socket ) +{ + RakAssert(passwordDataLength <= 256); + SystemAddress systemAddress; + systemAddress.FromStringExplicitPort(host,remotePort); + + // Already connected? + if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) + return ALREADY_CONNECTED_TO_ENDPOINT; + + //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); + RequestedConnectionStruct *rcs = MafiaNet::OP_NEW(_FILE_AND_LINE_); + + rcs->systemAddress=systemAddress; + rcs->nextRequestTime= MafiaNet::GetTimeMS(); + rcs->requestsMade=0; + rcs->data=0; + rcs->socket=0; + rcs->extraData=extraData; + rcs->socketIndex=connectionSocketIndex; + rcs->actionToTake=RequestedConnectionStruct::CONNECT; + rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; + rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; + // No password means passwordData is null. memcpy's source is declared nonnull, + // so a null source is undefined behaviour even with a length of 0. + if (passwordDataLength > 0) + memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); + rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; + rcs->timeoutTime=timeoutTime; + rcs->socket=socket; + +#if LIBCAT_SECURITY==1 + if (!GenerateConnectionRequestChallenge(rcs,publicKey)) + return SECURITY_INITIALIZATION_FAILED; +#else + (void) publicKey; +#endif + + // Return false if already pending, else push on queue + unsigned int i=0; + requestedConnectionQueueMutex.Lock(); + for (; i < requestedConnectionQueue.Size(); i++) + { + if (requestedConnectionQueue[i]->systemAddress==systemAddress) + { + requestedConnectionQueueMutex.Unlock(); + // Not necessary + //MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); + return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; + } + } + requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); + requestedConnectionQueueMutex.Unlock(); + + return CONNECTION_ATTEMPT_STARTED; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ValidateRemoteSystemLookup(void) const +{ +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystem( const AddressOrGUID systemIdentifier, bool calledFromNetworkThread, bool onlyActive ) const +{ + if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) + return GetRemoteSystemFromGUID(systemIdentifier.rakNetGuid, onlyActive); + else + return GetRemoteSystemFromSystemAddress(systemIdentifier.systemAddress, calledFromNetworkThread, onlyActive); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread, bool onlyActive ) const +{ + unsigned i; + + if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) + return 0; + + if (calledFromNetworkThread) + { + unsigned int index = GetRemoteSystemIndex(systemAddress); + if (index!=(unsigned int) -1) + { + if (onlyActive==false || remoteSystemList[ index ].isActive==true ) + { + RakAssert(remoteSystemList[index].systemAddress==systemAddress); + return remoteSystemList + index; + } + } + } + else + { + int deadConnectionIndex=-1; + + // Active connections take priority. But if there are no active connections, return the first systemAddress match found + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].systemAddress == systemAddress) + { + if ( remoteSystemList[ i ].isActive ) + return remoteSystemList + i; + else if (deadConnectionIndex==-1) + deadConnectionIndex=i; + } + } + + if (deadConnectionIndex!=-1 && onlyActive==false) + return remoteSystemList + deadConnectionIndex; + } + + return 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromGUID( const RakNetGUID guid, bool onlyActive ) const +{ + if (guid==UNASSIGNED_RAKNET_GUID) + return 0; + + unsigned i; + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].guid == guid && (onlyActive==false || remoteSystemList[ i ].isActive)) + { + return remoteSystemList + i; + } + } + return 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ParseConnectionRequestPacket( RakPeer::RemoteSystemStruct *remoteSystem, const SystemAddress &systemAddress, const char *data, int byteSize ) +{ + MafiaNet::BitStream bs((unsigned char*) data,byteSize,false); + bs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID guid; + bs.Read(guid); + MafiaNet::Time incomingTimestamp; + bs.Read(incomingTimestamp); + unsigned char doSecurity; + bs.Read(doSecurity); + +#if LIBCAT_SECURITY==1 + unsigned char doClientKey; + if (_using_security) + { + // Ignore message on bad state + if (doSecurity != 1 || !remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()) + return; + + // Validate client proof of key + unsigned char proof[cat::EasyHandshake::PROOF_BYTES]; + bs.ReadAlignedBytes(proof, sizeof(proof)); + if (!remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->ValidateProof(proof, sizeof(proof))) + { + remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + + CAT_OBJCLR(remoteSystem->client_public_key); + + bs.Read(doClientKey); + + // Check if client wants to prove identity + if (doClientKey == 1) + { + // Read identity proof + unsigned char ident[cat::EasyHandshake::IDENTITY_BYTES]; + bs.ReadAlignedBytes(ident, sizeof(ident)); + + // If we are listening to these proofs, + if (_require_client_public_key) + { + // Validate client identity + if (!_server_handshake->VerifyInitiatorIdentity(remoteSystem->answer, ident, remoteSystem->client_public_key)) + { + MafiaNet::BitStream bitStream; + bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect + bitStream.Write((unsigned char)2); // Indicate client identity is invalid + SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); + remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + } + + // Otherwise ignore the client public key + } + else + { + // If no client key was provided but it is required, + if (_require_client_public_key) + { + MafiaNet::BitStream bitStream; + bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect + bitStream.Write((unsigned char)1); // Indicate client identity is missing + SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); + remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + } + } +#endif // LIBCAT_SECURITY + + unsigned char *password = bs.GetData()+BITS_TO_BYTES(bs.GetReadOffset()); + int passwordLength = byteSize - BITS_TO_BYTES(bs.GetReadOffset()); + if ( incomingPasswordLength != passwordLength || + memcmp( password, incomingPassword, incomingPasswordLength ) != 0 ) + { + CAT_AUDIT_PRINTF("AUDIT: Invalid password\n"); + // This one we only send once since we don't care if it arrives. + MafiaNet::BitStream bitStream; + bitStream.Write((MessageID)ID_INVALID_PASSWORD); + bitStream.Write(GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + + // OK + remoteSystem->connectMode=RemoteSystemStruct::HANDLING_CONNECTION_REQUEST; + + OnConnectionRequest( remoteSystem, incomingTimestamp ); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::OnConnectionRequest( RakPeer::RemoteSystemStruct *remoteSystem, MafiaNet::Time incomingTimestamp ) +{ + MafiaNet::BitStream bitStream; + bitStream.Write((MessageID)ID_CONNECTION_REQUEST_ACCEPTED); + bitStream.Write(remoteSystem->systemAddress); + SystemIndex systemIndex = (SystemIndex) GetIndexFromSystemAddress( remoteSystem->systemAddress, true ); + RakAssert(systemIndex!=65535); + bitStream.Write(systemIndex); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + bitStream.Write(ipList[i]); + bitStream.Write(incomingTimestamp); + bitStream.Write(MafiaNet::GetTime()); + + SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, remoteSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::NotifyAndFlagForShutdown( const SystemAddress systemAddress, bool performImmediate, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority, const MafiaNet::BitStream *reasonData ) +{ + MafiaNet::BitStream temp( sizeof(unsigned char) ); + temp.Write( (MessageID)ID_DISCONNECTION_NOTIFICATION ); + // Optionally append a caller-supplied reason payload right after the 1-byte ID. The ID occupies exactly 8 bits so + // the payload stays byte-aligned; the remote reads it from packet->data+1 (length packet->length-1). Wire-backward + // compatible: peers that only inspect data[0] ignore the extra bytes. + if (reasonData != nullptr && reasonData->GetNumberOfBytesUsed() > 0) + temp.Write( (const char*)reasonData->GetData(), reasonData->GetNumberOfBytesUsed() ); + if (performImmediate) + { + SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, MafiaNet::Reliability::ReliableOrdered, orderingChannel, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); + RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(systemAddress, true, true); + rss->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; + } + else + { + SendBuffered((const char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, MafiaNet::Reliability::ReliableOrdered, orderingChannel, systemAddress, false, RemoteSystemStruct::DISCONNECT_ASAP, 0); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearDisconnectReason( RemoteSystemStruct *remoteSystem ) +{ + if (remoteSystem==0) + return; + if (remoteSystem->disconnectReasonData!=0) + { + rakFree_Ex(remoteSystem->disconnectReasonData, _FILE_AND_LINE_); + remoteSystem->disconnectReasonData=0; + } + remoteSystem->disconnectReasonLength=0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearSessionConfig( RemoteSystemStruct *remoteSystem ) +{ + if (remoteSystem==0) + return; + if (remoteSystem->remoteSessionConfigData!=0) + { + rakFree_Ex(remoteSystem->remoteSessionConfigData, _FILE_AND_LINE_); + remoteSystem->remoteSessionConfigData=0; + } + remoteSystem->remoteSessionConfigLength=0; + if (remoteSystem->withheldConnectionPacketData!=0) + { + rakFree_Ex(remoteSystem->withheldConnectionPacketData, _FILE_AND_LINE_); + remoteSystem->withheldConnectionPacketData=0; + } + remoteSystem->withheldConnectionPacketLength=0; + remoteSystem->sessionConfigAwaitingLocalDecision=false; + remoteSystem->sessionConfigIsConnectingSide=false; + remoteSystem->connectionReportedToApplication=false; + remoteSystem->sessionConfigStartTime=0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::StoreRemoteSessionConfig( RemoteSystemStruct *remoteSystem, const unsigned char *data, unsigned int length ) +{ + if (remoteSystem==0) + return false; + + // An oversized payload is a protocol violation, not something to truncate: the sender enforces the + // same cap in SetSessionConfig, so anything larger did not come from a well-behaved peer. + if (length > MAXIMUM_SESSION_CONFIG_SIZE) + return false; + + if (remoteSystem->remoteSessionConfigData!=0) + { + rakFree_Ex(remoteSystem->remoteSessionConfigData, _FILE_AND_LINE_); + remoteSystem->remoteSessionConfigData=0; + } + remoteSystem->remoteSessionConfigLength=0; + + if (data==0 || length==0) + return true; + + // One extra byte, always zeroed and never counted in the reported length. The payload is + // attacker-controlled and arbitrary bytes, so an application that reaches for strlen/printf on it + // would otherwise run off the end of the buffer. Length remains the authoritative bound. + remoteSystem->remoteSessionConfigData = (unsigned char*) rakMalloc_Ex(length + 1, _FILE_AND_LINE_); + if (remoteSystem->remoteSessionConfigData==0) + return false; + + memcpy(remoteSystem->remoteSessionConfigData, data, length); + remoteSystem->remoteSessionConfigData[length] = 0; + remoteSystem->remoteSessionConfigLength = length; + return true; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Replay the connection packet that was held back while the session handshake ran. This is the single +// point at which an application learns a connection exists, and by construction it runs only once the +// remote's session payload is already stored on the slot. +void RakPeer::ProduceWithheldConnectionPacket( RemoteSystemStruct *remoteSystem, MessageID messageId ) +{ + if (remoteSystem==0) + return; + + Packet *packet; + if (remoteSystem->withheldConnectionPacketData!=0 && remoteSystem->withheldConnectionPacketLength>0) + { + packet=AllocPacket(remoteSystem->withheldConnectionPacketLength, _FILE_AND_LINE_); + memcpy(packet->data, remoteSystem->withheldConnectionPacketData, remoteSystem->withheldConnectionPacketLength); + packet->bitSize = remoteSystem->withheldConnectionPacketLength * 8; + rakFree_Ex(remoteSystem->withheldConnectionPacketData, _FILE_AND_LINE_); + remoteSystem->withheldConnectionPacketData=0; + remoteSystem->withheldConnectionPacketLength=0; + } + else + { + // The original bytes could not be stashed (allocation failure); the message id alone still tells + // the application a connection exists, which is what every caller keys off. + packet=AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); + packet->data[0]=messageId; + packet->bitSize = sizeof(MessageID) * 8; + } + + packet->systemAddress = remoteSystem->systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex = packet->systemAddress.systemIndex; + remoteSystem->connectionReportedToApplication=true; + AddPacketToProducer(packet); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ProduceSessionAbandoned( RemoteSystemStruct *remoteSystem ) +{ + if (remoteSystem==0 || remoteSystem->weInitiatedTheConnection || remoteSystem->sessionConfigAwaitingLocalDecision==false) + return; + + // Once only: the flag is what made the request the application's to answer. + remoteSystem->sessionConfigAwaitingLocalDecision=false; + + Packet *packet=AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); + packet->data[0]=(unsigned char)ID_SESSION_CONFIG_ABANDONED; + packet->bitSize=sizeof(MessageID)*8; + packet->systemAddress=remoteSystem->systemAddress; + packet->systemAddress.systemIndex=remoteSystem->remoteSystemIndex; + packet->guid=remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Server side of the session handshake: answer the client's request, promote the connection to +// CONNECTED and release the ID_NEW_INCOMING_CONNECTION that was withheld. +void RakPeer::SendSessionConfigResponse( RemoteSystemStruct *remoteSystem, const char *data, unsigned int length ) +{ + if (remoteSystem==0) + return; + + MafiaNet::BitStream bitStream; + bitStream.Write((MessageID)ID_SESSION_CONFIG); + if (data && length > 0) + bitStream.Write(data, length); + SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, remoteSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); + + remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; + remoteSystem->sessionConfigStartTime=0; + ProduceWithheldConnectionPacket(remoteSystem, (MessageID)ID_NEW_INCOMING_CONNECTION); + + PingInternal( remoteSystem->systemAddress, true, MafiaNet::Reliability::Unreliable ); + quitAndDataEvents.SetEvent(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendSessionConfigRejection( RemoteSystemStruct *remoteSystem, const char *reason, unsigned int reasonLength ) +{ + if (remoteSystem==0) + return; + + MafiaNet::BitStream bitStream; + bitStream.Write((MessageID)ID_SESSION_CONFIG_REJECTED); + if (reason && reasonLength > 0) + bitStream.Write(reason, reasonLength); + SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, remoteSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); + + // The refusal has to reach the client before the socket goes away, so drop on no-ack rather than + // silently: the client turns ID_SESSION_CONFIG_REJECTED into ID_CONNECTION_ATTEMPT_FAILED itself. + remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ON_NO_ACK; + remoteSystem->sessionConfigStartTime = 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetNumberOfRemoteInitiatedConnections( void ) const +{ + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + unsigned int numberOfIncomingConnections; + numberOfIncomingConnections = 0; + unsigned int i; + for (i=0; i < activeSystemListSize; i++) + { + if ((activeSystemList[i])->isActive && + (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED && + (activeSystemList[i])->weInitiatedTheConnection==false + ) + { + numberOfIncomingConnections++; + } + } + return numberOfIncomingConnections; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct * RakPeer::AssignSystemAddressToRemoteSystemList( const SystemAddress systemAddress, RemoteSystemStruct::ConnectMode connectionMode, RakNetSocket2* incomingRakNetSocket, bool *thisIPConnectedRecently, SystemAddress bindingAddress, int incomingMTU, RakNetGUID guid, bool useSecurity ) +{ + RemoteSystemStruct * remoteSystem; + unsigned i,j,assignedIndex; + MafiaNet::TimeMS time = MafiaNet::GetTimeMS(); +#ifdef _DEBUG + RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); +#endif + + if (limitConnectionFrequencyFromTheSameIP) + { + if (IsLoopbackAddress(systemAddress,false)==false) + { + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if ( remoteSystemList[ i ].isActive==true && + remoteSystemList[ i ].systemAddress.EqualsExcludingPort(systemAddress) && + time >= remoteSystemList[ i ].connectionTime && + time - remoteSystemList[ i ].connectionTime < 100 + ) + { + // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots + // Ignore connection attempts if this IP address connected within the last 100 milliseconds + *thisIPConnectedRecently=true; + ValidateRemoteSystemLookup(); + return 0; + } + } + } + } + + // Don't use a different port than what we received on + bindingAddress.CopyPort(incomingRakNetSocket->GetBoundAddress()); + + *thisIPConnectedRecently=false; + for ( assignedIndex = 0; assignedIndex < maximumNumberOfPeers; assignedIndex++ ) + { + if ( remoteSystemList[ assignedIndex ].isActive==false ) + { + // printf("--- Address %s has become active\n", systemAddress.ToString()); + + remoteSystem=remoteSystemList+assignedIndex; + ReferenceRemoteSystem(systemAddress, assignedIndex); + // Stale reason payload from a prior occupant of this slot must never leak into a new connection. + ClearDisconnectReason(remoteSystem); + // Same for session state: peer.h documents it as cleared on slot reuse, and only the teardown + // paths do it today. Without this, any future path that deactivates a slot without going + // through CloseConnectionInternal2 would hand the previous occupant payload to the new one. + ClearSessionConfig(remoteSystem); + remoteSystem->MTUSize=defaultMTUSize; + remoteSystem->guid=guid; + remoteSystem->isActive = true; // This one line causes future incoming packets to go through the reliability layer + // Reserve this reliability layer for ourselves. + // + // incomingMTU comes off the wire -- ID_OPEN_CONNECTION_REQUEST_2 on the accepting side, + // ID_OPEN_CONNECTION_REPLY_2 on the connecting one -- so it is whatever the remote peer + // said, and must be clamped rather than trusted. MTUSize sizes every datagram the + // reliability layer builds into buffers declared MAXIMUM_MTU_SIZE bytes long, so a + // larger value writes past the end of them. A peer built against a higher cap is enough + // to produce one; a hostile peer can name any uint16. This used to be a bare RakAssert, + // which is compiled out of exactly the builds that ship. + if (incomingMTU > MAXIMUM_MTU_SIZE) + incomingMTU = MAXIMUM_MTU_SIZE; + if (incomingMTU > remoteSystem->MTUSize) + remoteSystem->MTUSize=incomingMTU; + RakAssert(remoteSystem->MTUSize <= MAXIMUM_MTU_SIZE); + remoteSystem->reliabilityLayer.Reset(true, remoteSystem->MTUSize, useSecurity); + remoteSystem->reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); + remoteSystem->reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); + remoteSystem->reliabilityLayer.SetTimeoutTime(defaultTimeoutTime); + AddToActiveSystemList(assignedIndex); + if (incomingRakNetSocket->GetBoundAddress()==bindingAddress) + { + remoteSystem->rakNetSocket=incomingRakNetSocket; + } + else + { + // Originally this searched ipList for the internal address the datagram came in on and + // forced the reply onto it, so a multi-homed machine answers from the same IP. That was + // disabled upstream (06/26/09, unconfirmed report that the Vista firewall blocks the reply + // if we force a binding), which left the search computing an index nobody read. Always + // reply on the socket the datagram came in on. + remoteSystem->rakNetSocket=incomingRakNetSocket; + } + + for ( j = 0; j < (unsigned) PING_TIMES_ARRAY_SIZE; j++ ) + { + remoteSystem->pingAndClockDifferential[ j ].pingTime = 65535; + remoteSystem->pingAndClockDifferential[ j ].clockDifferential = 0; + } + + remoteSystem->connectMode=connectionMode; + remoteSystem->pingAndClockDifferentialWriteIndex = 0; + remoteSystem->lowestPing = 65535; + remoteSystem->nextPingTime = 0; // Ping immediately + remoteSystem->weInitiatedTheConnection = false; + remoteSystem->connectionTime = time; + remoteSystem->myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; + remoteSystem->lastReliableSend=time; + +#ifdef _DEBUG + int indexLoopupCheck=GetIndexFromSystemAddress( systemAddress, true ); + if ((int) indexLoopupCheck!=(int) assignedIndex) + { + RakAssert((int) indexLoopupCheck==(int) assignedIndex); + } +#endif + + return remoteSystem; + } + } + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Adjust the first four bytes (treated as unsigned int) of the pointer +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ShiftIncomingTimestamp( unsigned char *data, const SystemAddress &systemAddress ) const +{ +#ifdef _DEBUG + RakAssert( IsActive() ); + RakAssert( data ); +#endif + + MafiaNet::BitStream timeBS( data, sizeof(MafiaNet::Time), false); + MafiaNet::Time encodedTimestamp; + timeBS.Read(encodedTimestamp); + + encodedTimestamp = encodedTimestamp - GetBestClockDifferential( systemAddress ); + timeBS.SetWriteOffset(0); + timeBS.Write(encodedTimestamp); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm +MafiaNet::Time RakPeer::GetBestClockDifferential( const SystemAddress systemAddress ) const +{ + RemoteSystemStruct *remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + + if ( remoteSystem == 0 ) + return 0; + + return GetClockDifferentialInt(remoteSystem); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::RemoteSystemLookupHashIndex(const SystemAddress &sa) const +{ + return SystemAddress::ToInteger(sa) % ((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ReferenceRemoteSystem(const SystemAddress &sa, unsigned int remoteSystemListIndex) +{ +// #ifdef _DEBUG +// for ( unsigned int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) +// { +// if (remoteSystemList[remoteSystemIndex].isActive ) +// { +// unsigned int debugHashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); +// RakAssert(debugHashIndex==remoteSystemIndex); +// } +// } +// #endif + + + SystemAddress oldAddress = remoteSystemList[remoteSystemListIndex].systemAddress; + if (oldAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + // The system might be active if rerouting +// RakAssert(remoteSystemList[remoteSystemListIndex].isActive==false); + + // Remove the reference if the reference is pointing to this inactive system + if (GetRemoteSystem(oldAddress)==&remoteSystemList[remoteSystemListIndex]) + DereferenceRemoteSystem(oldAddress); + } + DereferenceRemoteSystem(sa); + +// #ifdef _DEBUG +// for ( unsigned int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) +// { +// if (remoteSystemList[remoteSystemIndex].isActive ) +// { +// unsigned int debugHashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); +// if (debugHashIndex!=remoteSystemIndex) +// { +// RakAssert(debugHashIndex==remoteSystemIndex); +// } +// } +// } +// #endif + + + remoteSystemList[remoteSystemListIndex].systemAddress=sa; + + unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); + RemoteSystemIndex *rsi; + rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); + if (remoteSystemLookup[hashIndex]==0) + { + rsi->next=0; + rsi->index=remoteSystemListIndex; + remoteSystemLookup[hashIndex]=rsi; + } + else + { + RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; + while (cur->next!=0) + { + cur=cur->next; + } + + rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); + rsi->next=0; + rsi->index=remoteSystemListIndex; + cur->next=rsi; + } + +// #ifdef _DEBUG +// for ( unsigned int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) +// { +// if (remoteSystemList[remoteSystemIndex].isActive ) +// { +// unsigned int debugHashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); +// RakAssert(debugHashIndex==remoteSystemIndex); +// } +// } +// #endif + + + RakAssert(GetRemoteSystemIndex(sa)==remoteSystemListIndex); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DereferenceRemoteSystem(const SystemAddress &sa) +{ + unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); + RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; + RemoteSystemIndex *last = 0; + while (cur!=0) + { + if (remoteSystemList[cur->index].systemAddress==sa) + { + if (last==0) + { + remoteSystemLookup[hashIndex]=cur->next; + } + else + { + last->next=cur->next; + } + remoteSystemIndexPool.Release(cur,_FILE_AND_LINE_); + break; + } + last=cur; + cur=cur->next; + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetRemoteSystemIndex(const SystemAddress &sa) const +{ + unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); + RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; + while (cur!=0) + { + if (remoteSystemList[cur->index].systemAddress==sa) + return cur->index; + cur=cur->next; + } + return (unsigned int) -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct* RakPeer::GetRemoteSystem(const SystemAddress &sa) const +{ + unsigned int remoteSystemIndex = GetRemoteSystemIndex(sa); + if (remoteSystemIndex==(unsigned int)-1) + return 0; + return remoteSystemList + remoteSystemIndex; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearRemoteSystemLookup(void) +{ + remoteSystemIndexPool.Clear(_FILE_AND_LINE_); + MafiaNet::OP_DELETE_ARRAY(remoteSystemLookup,_FILE_AND_LINE_); + remoteSystemLookup=0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AddToActiveSystemList(unsigned int remoteSystemListIndex) +{ + activeSystemList[activeSystemListSize++]=remoteSystemList+remoteSystemListIndex; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RemoveFromActiveSystemList(const SystemAddress &sa) +{ + unsigned int i; + for (i=0; i < activeSystemListSize; i++) + { + RemoteSystemStruct *rss=activeSystemList[i]; + if (rss->systemAddress==sa) + { + activeSystemList[i]=activeSystemList[activeSystemListSize-1]; + activeSystemListSize--; + return; + } + } + RakAssert("activeSystemList invalid, entry not found in RemoveFromActiveSystemList. Ensure that AddToActiveSystemList and RemoveFromActiveSystemList are called by the same thread." && 0); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +/* +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::LookupIndexUsingHashIndex(const SystemAddress &sa) const +{ + unsigned int scanCount=0; + unsigned int index = RemoteSystemLookupHashIndex(sa); + if (remoteSystemLookup[index].index==(unsigned int)-1) + return (unsigned int) -1; + while (remoteSystemList[remoteSystemLookup[index].index].systemAddress!=sa) + { + if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) + index=0; + if (++scanCount>(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) + return (unsigned int) -1; + if (remoteSystemLookup[index].index==-1) + return (unsigned int) -1; + } + return index; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::RemoteSystemListIndexUsingHashIndex(const SystemAddress &sa) const +{ + unsigned int index = LookupIndexUsingHashIndex(sa); + if (index!=(unsigned int) -1) + { + return remoteSystemLookup[index].index; + } + return (unsigned int) -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::FirstFreeRemoteSystemLookupIndex(const SystemAddress &sa) const +{ +// unsigned int collisionCount=0; + unsigned int index = RemoteSystemLookupHashIndex(sa); + while (remoteSystemLookup[index].index!=(unsigned int)-1) + { + if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) + index=0; +// collisionCount++; + } +// printf("%i collisions. Using index %i\n", collisionCount, index); + return index; +} +*/ +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsLoopbackAddress(const AddressOrGUID &systemIdentifier, bool matchPort) const +{ + if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) + return systemIdentifier.rakNetGuid==myGuid; + + for (int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS && ipList[i]!=UNASSIGNED_SYSTEM_ADDRESS; i++) + { + if (matchPort) + { + if (ipList[i]==systemIdentifier.systemAddress) + return true; + } + else + { + if (ipList[i].EqualsExcludingPort(systemIdentifier.systemAddress)) + return true; + } + } + + return (matchPort==true && systemIdentifier.systemAddress==firstExternalID) || + (matchPort==false && systemIdentifier.systemAddress.EqualsExcludingPort(firstExternalID)); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetLoopbackAddress(void) const +{ + + return ipList[0]; + + + +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::AllowIncomingConnections(void) const +{ + // Counts peers still running the session handshake as well as fully established ones. + // GetNumberOfRemoteInitiatedConnections() deliberately reports only CONNECTED peers -- that is what + // an application means by "players" -- but admission control must not share that view: a peer parked + // in EXCHANGING_SESSION_DATA already owns a slot, and counting only CONNECTED would let peers that + // stall the handshake push the real total past SetMaximumIncomingConnections(). + if ( remoteSystemList == 0 || endThreads == true ) + return false; + + return AllowIncomingConnection(UNASSIGNED_SYSTEM_ADDRESS); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::AllowIncomingConnection(const SystemAddress &address) const +{ + if ( remoteSystemList == 0 || endThreads == true ) + return false; + + SessionAdmission::Limits limits; + limits.maxIncoming = GetMaximumIncomingConnections(); + limits.maxPending = maximumPendingSessions; + limits.maxPendingPerAddress = maximumPendingSessionsPerAddress; + + SessionAdmission::Counts counts; + CountRemoteInitiatedPeers(address, counts.connected, counts.exchanging, counts.pending, counts.pendingFromAddress); + return SessionAdmission::MayStartConnecting(limits, counts); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// A pending peer is one that asked to connect and is not CONNECTED yet: still proving the transport +// (UNVERIFIED_SENDER, HANDLING_CONNECTION_REQUEST) or in the session handshake. A peer being torn down is +// in none of these, so a refusal frees its share of the pool at once rather than after the ack. +void RakPeer::CountRemoteInitiatedPeers(const SystemAddress &address, unsigned int &connected, unsigned int &exchanging, unsigned int &pending, unsigned int &pendingFromAddress) const +{ + connected = exchanging = pending = pendingFromAddress = 0; + for (unsigned int i=0; i < activeSystemListSize; i++) + { + const RemoteSystemStruct *system = activeSystemList[i]; + if (!system->isActive || system->weInitiatedTheConnection) + continue; + + switch (system->connectMode) + { + case RemoteSystemStruct::CONNECTED: + connected++; + break; + case RemoteSystemStruct::EXCHANGING_SESSION_DATA: + exchanging++; + // A peer in the handshake is pending too. + [[fallthrough]]; + case RemoteSystemStruct::UNVERIFIED_SENDER: + case RemoteSystemStruct::HANDLING_CONNECTION_REQUEST: + pending++; + if (address!=UNASSIGNED_SYSTEM_ADDRESS && system->systemAddress.EqualsExcludingPort(address)) + pendingFromAddress++; + break; + default: + break; + } + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +MafiaNet::TimeMS RakPeer::GetSessionTimeoutFor(RemoteSystemStruct *remoteSystem) const +{ + return sessionTimeout != 0 ? sessionTimeout : remoteSystem->reliabilityLayer.GetTimeoutTime(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DeallocRNS2RecvStruct(RNS2RecvStruct *s, const char *file, unsigned int line) +{ + bufferedPacketsFreePoolMutex.Lock(); + bufferedPacketsFreePool.Push(s, file, line); + bufferedPacketsFreePoolMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RNS2RecvStruct *RakPeer::AllocRNS2RecvStruct(const char *file, unsigned int line) +{ + bufferedPacketsFreePoolMutex.Lock(); + if (bufferedPacketsFreePool.Size()>0) + { + RNS2RecvStruct *s = bufferedPacketsFreePool.Pop(); + bufferedPacketsFreePoolMutex.Unlock(); + return s; + } + else + { + bufferedPacketsFreePoolMutex.Unlock(); + return MafiaNet::OP_NEW(file,line); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearBufferedPackets(void) +{ + bufferedPacketsFreePoolMutex.Lock(); + while (bufferedPacketsFreePool.Size()>0) + MafiaNet::OP_DELETE(bufferedPacketsFreePool.Pop(), _FILE_AND_LINE_); + bufferedPacketsFreePoolMutex.Unlock(); + + bufferedPacketsQueueMutex.Lock(); + while (bufferedPacketsQueue.Size()>0) + MafiaNet::OP_DELETE(bufferedPacketsQueue.Pop(), _FILE_AND_LINE_); + bufferedPacketsQueueMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetupBufferedPackets(void) +{ +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::PushBufferedPacket(RNS2RecvStruct * p) +{ + bufferedPacketsQueueMutex.Lock(); + bufferedPacketsQueue.Push(p, _FILE_AND_LINE_); + bufferedPacketsQueueMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RNS2RecvStruct *RakPeer::PopBufferedPacket(void) +{ + bufferedPacketsQueueMutex.Lock(); + if (bufferedPacketsQueue.Size()>0) + { + RNS2RecvStruct *s = bufferedPacketsQueue.Pop(); + bufferedPacketsQueueMutex.Unlock(); + return s; + } + bufferedPacketsQueueMutex.Unlock(); + return 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::PingInternal( const SystemAddress target, bool performImmediate, MafiaNet::Reliability reliability ) +{ + if ( IsActive() == false ) + return ; + + MafiaNet::BitStream bitStream(sizeof(unsigned char)+sizeof(MafiaNet::Time)); + bitStream.Write((MessageID)ID_CONNECTED_PING); + bitStream.Write(MafiaNet::GetTime()); + if (performImmediate) + SendImmediate( (char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, reliability, 0, target, false, false, MafiaNet::GetTimeUS(), 0 ); + else + Send( &bitStream, MafiaNet::Priority::Immediate, reliability, 0, target, false ); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::CloseConnectionInternal( const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority ) +{ + RakAssert(orderingChannel < 32); + + if (systemIdentifier.IsUndefined()) + return; + + if (remoteSystemList == 0 || endThreads == true) + return; + + // #med - this is flawed and needs to be resolved properly - socketList[0] is not correct here + CloseConnectionInternal2(systemIdentifier, sendDisconnectionNotification, performImmediate, orderingChannel, disconnectionNotificationPriority, *(socketList[0])); +} + +// #med - better integrate directly in CloseConnectionInternal2() +void RakPeer::CloseConnectionInternal2(const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority, RakNetSocket2& socket, const MafiaNet::BitStream *reasonData) +{ + RakAssert(orderingChannel < 32); + + if (systemIdentifier.IsUndefined()) + return; + + if ( remoteSystemList == 0 || endThreads == true ) + return; + + SystemAddress target; + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + target=systemIdentifier.systemAddress; + } + else + { + target=GetSystemAddressFromGuid(systemIdentifier.rakNetGuid); + } + + if (target != UNASSIGNED_SYSTEM_ADDRESS && performImmediate) { + target.FixForIPVersion(socket.GetBoundAddress()); + } + + if (sendDisconnectionNotification) + { + NotifyAndFlagForShutdown(target, performImmediate, orderingChannel, disconnectionNotificationPriority, reasonData); + } + else + { + if (performImmediate) + { + unsigned int index = GetRemoteSystemIndex(target); + if (index!=(unsigned int) -1) + { + if ( remoteSystemList[index].isActive ) + { + RemoveFromActiveSystemList(target); + + // Found the index to stop + // printf("--- Address %s has become inactive\n", remoteSystemList[index].systemAddress.ToString()); + remoteSystemList[index].isActive = false; + + remoteSystemList[index].guid=UNASSIGNED_RAKNET_GUID; + + // Reserve this reliability layer for ourselves + //remoteSystemList[ remoteSystemLookup[index].index ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; + + // Clear any remaining messages + RakAssert(remoteSystemList[index].MTUSize <= MAXIMUM_MTU_SIZE); + remoteSystemList[index].reliabilityLayer.Reset(false, remoteSystemList[index].MTUSize, false); + + // Free any stashed disconnect-reason payload (e.g. delivered already, or never consumed) + ClearDisconnectReason(&remoteSystemList[index]); + + // Free any session-handshake state left over from an exchange that never completed + ClearSessionConfig(&remoteSystemList[index]); + + // Not using this socket + remoteSystemList[index].rakNetSocket = 0; + } + } + } + else + { + BufferedCommandStruct *bcs; + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->command=BufferedCommandStruct::BCS_CLOSE_CONNECTION; + bcs->systemIdentifier=target; + // #med - review whether this is safe here - can't the socket be closed/released before its used? + bcs->socket = &socket; + bcs->data=0; + bcs->orderingChannel=orderingChannel; + bcs->priority=disconnectionNotificationPriority; + bufferedCommands.Push(bcs); + } + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendBuffered( const char *data, BitSize_t numberOfBitsToSend, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) +{ + BufferedCommandStruct *bcs; + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->data = (char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES(numberOfBitsToSend), _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy + if (bcs->data==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); + return; + } + + RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); + RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); + RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); + + memcpy(bcs->data, data, (size_t) BITS_TO_BYTES(numberOfBitsToSend)); + bcs->numberOfBitsToSend=numberOfBitsToSend; + bcs->priority=priority; + bcs->reliability=reliability; + bcs->orderingChannel=orderingChannel; + bcs->systemIdentifier=systemIdentifier; + bcs->broadcast=broadcast; + bcs->connectionMode=connectionMode; + bcs->receipt=receipt; + bcs->command=BufferedCommandStruct::BCS_SEND; + bufferedCommands.Push(bcs); + + if (priority==MafiaNet::Priority::Immediate) + { + // Forces pending sends to go out now, rather than waiting to the next update interval + quitAndDataEvents.SetEvent(); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendBufferedList( const char **data, const int *lengths, const int numParameters, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) +{ + BufferedCommandStruct *bcs; + unsigned int totalLength=0; + unsigned int lengthOffset; + int i; + for (i=0; i < numParameters; i++) + { + if (lengths[i]>0) + totalLength+=lengths[i]; + } + if (totalLength==0) + return; + + char *dataAggregate; + dataAggregate = (char*) rakMalloc_Ex( (size_t) totalLength, _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy + if (dataAggregate==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + return; + } + for (i=0, lengthOffset=0; i < numParameters; i++) + { + if (lengths[i]>0) + { + memcpy(dataAggregate+lengthOffset, data[i], lengths[i]); + lengthOffset+=lengths[i]; + } + } + + if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) + { + SendLoopback(dataAggregate,totalLength); + rakFree_Ex(dataAggregate,_FILE_AND_LINE_); + return; + } + + RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); + RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); + RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->data = dataAggregate; + bcs->numberOfBitsToSend=BYTES_TO_BITS(totalLength); + bcs->priority=priority; + bcs->reliability=reliability; + bcs->orderingChannel=orderingChannel; + bcs->systemIdentifier=systemIdentifier; + bcs->broadcast=broadcast; + bcs->connectionMode=connectionMode; + bcs->receipt=receipt; + bcs->command=BufferedCommandStruct::BCS_SEND; + bufferedCommands.Push(bcs); + + if (priority==MafiaNet::Priority::Immediate) + { + // Forces pending sends to go out now, rather than waiting to the next update interval + quitAndDataEvents.SetEvent(); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::SendImmediate( char *data, BitSize_t numberOfBitsToSend, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool useCallerDataAllocation, MafiaNet::TimeUS currentTime, uint32_t receipt ) +{ + unsigned *sendList; + unsigned sendListSize; + bool callerDataAllocationUsed; + unsigned int remoteSystemIndex, sendListIndex; // Iterates into the list of remote systems +// unsigned numberOfBytesUsed = (unsigned) BITS_TO_BYTES(numberOfBitsToSend); + callerDataAllocationUsed=false; + + sendListSize=0; + + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + remoteSystemIndex=GetIndexFromSystemAddress( systemIdentifier.systemAddress, true ); + else if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) + remoteSystemIndex=GetSystemIndexFromGuid(systemIdentifier.rakNetGuid); + else + remoteSystemIndex=(unsigned int) -1; + + // 03/06/06 - If broadcast is false, use the optimized version of GetIndexFromSystemAddress + if (broadcast==false) + { + if (remoteSystemIndex==(unsigned int) -1) + { +#ifdef _DEBUG +// int debugIndex = GetRemoteSystemIndex(systemIdentifier.systemAddress); +#endif + return false; + } + + #if USE_ALLOCA==1 + sendList=(unsigned *)alloca(sizeof(unsigned)); + #else + sendList = (unsigned *) rakMalloc_Ex(sizeof(unsigned), _FILE_AND_LINE_); + #endif + + if (remoteSystemList[remoteSystemIndex].isActive && + remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP && + remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY && + remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ON_NO_ACK) + { + sendList[0]=remoteSystemIndex; + sendListSize=1; + } + } + else + { + #if USE_ALLOCA==1 + sendList=(unsigned *)alloca(sizeof(unsigned)*maximumNumberOfPeers); + #else + sendList = (unsigned *) rakMalloc_Ex(sizeof(unsigned)*maximumNumberOfPeers, _FILE_AND_LINE_); + #endif + + // remoteSystemList in network thread + unsigned int idx; + for ( idx = 0; idx < maximumNumberOfPeers; idx++ ) + { + if (remoteSystemIndex!=(unsigned int) -1 && idx==remoteSystemIndex) + continue; + + // Peers still running the session handshake are excluded: the application has not been told they + // exist, so broadcasting to them would hand application data to a peer it never accepted -- and + // possibly to one it is about to refuse with RejectSession(). Internal handshake traffic is always + // directed, so it never reaches this branch. + if ( remoteSystemList[ idx ].isActive && remoteSystemList[ idx ].systemAddress != UNASSIGNED_SYSTEM_ADDRESS && + remoteSystemList[ idx ].connectMode != RemoteSystemStruct::EXCHANGING_SESSION_DATA ) + sendList[sendListSize++]=idx; + } + } + + if (sendListSize==0) + { + #if !defined(USE_ALLOCA) + rakFree_Ex(sendList, _FILE_AND_LINE_ ); + #endif + + return false; + } + + for (sendListIndex=0; sendListIndex < sendListSize; sendListIndex++) + { + // Send may split the packet and thus deallocate data. Don't assume data is valid if we use the callerAllocationData + bool useData = useCallerDataAllocation && callerDataAllocationUsed==false && sendListIndex+1==sendListSize; + remoteSystemList[sendList[sendListIndex]].reliabilityLayer.Send( data, numberOfBitsToSend, priority, reliability, orderingChannel, useData==false, remoteSystemList[sendList[sendListIndex]].MTUSize, currentTime, receipt ); + if (useData) + callerDataAllocationUsed=true; + + if (reliability==MafiaNet::Reliability::Reliable || + reliability==MafiaNet::Reliability::ReliableOrdered || + reliability==MafiaNet::Reliability::ReliableSequenced || + reliability==MafiaNet::Reliability::ReliableWithAckReceipt || + reliability==MafiaNet::Reliability::ReliableOrderedWithAckReceipt +// || +// reliability==RELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) + remoteSystemList[sendList[sendListIndex]].lastReliableSend=(MafiaNet::TimeMS)(currentTime/(MafiaNet::TimeUS)1000); + } + +#if !defined(USE_ALLOCA) + rakFree_Ex(sendList, _FILE_AND_LINE_ ); +#endif + + // Return value only meaningful if true was passed for useCallerDataAllocation. Means the reliability layer used that data copy, so the caller should not deallocate it + return callerDataAllocationUsed; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ResetSendReceipt(void) +{ + sendReceiptSerialMutex.Lock(); + sendReceiptSerial=1; + sendReceiptSerialMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::OnConnectedPong(MafiaNet::Time sendPingTime, MafiaNet::Time sendPongTime, RemoteSystemStruct *remoteSystem) +{ + MafiaNet::Time ping; +// MafiaNet::TimeMS lastPing; + MafiaNet::Time time = MafiaNet::GetTime(); // Update the time value to be accurate + if (time > sendPingTime) + ping = time - sendPingTime; + else + ping=0; + +// lastPing = remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime; + + remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime = ( unsigned short ) ping; + // Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm + // Divide each integer by 2, rather than the sum by 2, to prevent overflow + remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].clockDifferential = sendPongTime - ( time/2 + sendPingTime/2 ); + + if ( remoteSystem->lowestPing == (unsigned short)-1 || remoteSystem->lowestPing > (int) ping ) + remoteSystem->lowestPing = (unsigned short) ping; + + if ( ++( remoteSystem->pingAndClockDifferentialWriteIndex ) == (MafiaNet::Time) PING_TIMES_ARRAY_SIZE ) + remoteSystem->pingAndClockDifferentialWriteIndex = 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearBufferedCommands(void) +{ + BufferedCommandStruct *bcs; + + while ((bcs=bufferedCommands.Pop())!=0) + { + if (bcs->data) + rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); + + bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); + } + bufferedCommands.Clear(_FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearSocketQueryOutput(void) +{ + socketQueryOutput.Clear(_FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearRequestedConnectionList(void) +{ + DataStructures::Queue freeQueue; + requestedConnectionQueueMutex.Lock(); + while (requestedConnectionQueue.Size()) + freeQueue.Push(requestedConnectionQueue.Pop(), _FILE_AND_LINE_ ); + requestedConnectionQueueMutex.Unlock(); + unsigned i; + for (i=0; i < freeQueue.Size(); i++) + { +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: In ClearRequestedConnectionList(), Deleting freeQueue index %i client_handshake %x\n", i, freeQueue[i]->client_handshake); + MafiaNet::OP_DELETE(freeQueue[i]->client_handshake,_FILE_AND_LINE_); +#endif + MafiaNet::OP_DELETE(freeQueue[i], _FILE_AND_LINE_ ); + } +} +bool RakPeer::IsInsideNetworkThreadReceive(void) const +{ + return networkThreadDispatcher.load()==std::this_thread::get_id(); +} + +void RakPeer::SetNetworkThreadReceiveStopped(bool stopped) +{ + pluginListNetworkThreadMutex.Lock(); + networkThreadReceiveStopped = stopped; + pluginListNetworkThreadMutex.Unlock(); +} + +bool RakPeer::DispatchNetworkThreadReceive(Packet *packet) +{ + pluginListNetworkThreadMutex.Lock(); + if (networkThreadReceiveStopped) + { + pluginListNetworkThreadMutex.Unlock(); + return false; + } + + networkThreadDispatcher = std::this_thread::get_id(); + PluginReceiveResult result = RR_CONTINUE_PROCESSING; + for (unsigned int i=0; i < pluginListNetworkThread.Size() && result==RR_CONTINUE_PROCESSING; i++) + result = pluginListNetworkThread[i]->OnReceiveNetworkThread(packet); + networkThreadDispatcher = std::thread::id(); + pluginListNetworkThreadMutex.Unlock(); + + if (result==RR_STOP_PROCESSING_AND_DEALLOCATE) + DeallocatePacket(packet); + return result!=RR_CONTINUE_PROCESSING; +} + +inline void RakPeer::AddPacketToProducer(MafiaNet::Packet *p) +{ + packetReturnMutex.Lock(); + packetReturnQueue.Push(p,_FILE_AND_LINE_); + packetReturnMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +union Buff6AndBuff8 +{ + unsigned char buff6[6]; + uint64_t buff8; +}; +uint64_t RakPeerInterface::Get64BitUniqueRandomNumber(void) +{ + // Mac address is a poor solution because you can't have multiple connections from the same system + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#if defined(_WIN32) + uint64_t g= MafiaNet::GetTimeUS(); + + MafiaNet::TimeUS lastTime, thisTime; + int j; + // Sleep a small random time, then use the last 4 bits as a source of randomness + for (j=0; j < 8; j++) + { + lastTime = MafiaNet::GetTimeUS(); + RakSleep(1); + RakSleep(0); + thisTime = MafiaNet::GetTimeUS(); + MafiaNet::TimeUS diff = thisTime-lastTime; + unsigned int diff4Bits = (unsigned int) (diff & 15); + diff4Bits <<= 32-4; + diff4Bits >>= j*4; + ((char*)&g)[j] ^= diff4Bits; + } + // Timer jitter alone collides when peers are created back to back, and is + // constant under a test's virtual clock. Mix in the same entropy as below. + static std::atomic sequence {0}; + std::random_device entropy; + g ^= ((uint64_t)entropy() << 32) ^ (uint64_t)entropy(); + g ^= (sequence.fetch_add(1, std::memory_order_relaxed) + 1) * 0x9E3779B97F4A7C15ull; + if (g == 0 || g == (uint64_t)-1) + g = 1; + return g; + +#else + // The time alone is not unique: two peers constructed within the same microsecond -- a server + // and a client created back to back in one process -- got the same GUID, and a peer then + // resolved the other's GUID to itself, so CloseConnection() and friends silently did nothing. + // Mix in the OS entropy source and a per-process counter. + struct timeval tv; + gettimeofday(&tv, nullptr); + uint64_t g = (uint64_t)tv.tv_usec + (uint64_t)tv.tv_sec * 1000000; + + static std::atomic sequence {0}; + std::random_device entropy; + g ^= ((uint64_t)entropy() << 32) ^ (uint64_t)entropy(); + g ^= (sequence.fetch_add(1, std::memory_order_relaxed) + 1) * 0x9E3779B97F4A7C15ull; + + // Neither value is a usable GUID: 0 means "not generated" and all-ones is UNASSIGNED_RAKNET_GUID. + if (g == 0 || g == (uint64_t)-1) + g = 1; + return g; +#endif +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GenerateGUID(void) +{ + myGuid.g=Get64BitUniqueRandomNumber(); + +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// void MafiaNet::ProcessPortUnreachable( SystemAddress systemAddress, RakPeer *rakPeer ) +// { +// (void) binaryAddress; +// (void) port; +// (void) rakPeer; +// +// } +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +namespace MafiaNet { +bool ProcessOfflineNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSocket2* rakNetSocket, bool *isOfflineMessage, MafiaNet::TimeUS timeRead ) +{ + (void) timeRead; + RakPeer::RemoteSystemStruct *remoteSystem; + MafiaNet::Packet *packet; + unsigned i; + + + char str1[64]; + systemAddress.ToString(false, str1,static_cast(64)); + if (rakPeer->IsBanned( str1 )) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + MafiaNet::BitStream bs; + bs.Write((MessageID)ID_CONNECTION_BANNED); + bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + + + RNS2_SendParameters bsp; + bsp.data = (char*) bs.GetData(); + bsp.length = bs.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*) bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + +/* + unsigned i; + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*) bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket, (char*) bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + */ + + return true; + } + + + + // The reason for all this is that the reliability layer has no way to tell between offline messages that arrived late for a player that is now connected, + // and a regular encoding. So I insert OFFLINE_MESSAGE_DATA_ID into the stream, the encoding of which is essentially impossible to hit by chance + if (length <=2) + { + *isOfflineMessage=true; + } + else if ( + ((unsigned char)data[0] == ID_UNCONNECTED_PING || + (unsigned char)data[0] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS) && + (size_t) length >= sizeof(unsigned char) + sizeof(MafiaNet::Time) + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(MafiaNet::Time), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if ((unsigned char)data[0] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char) + sizeof(MafiaNet::TimeMS) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(MafiaNet::Time) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if ( + (unsigned char)data[0] == ID_OUT_OF_BAND_INTERNAL && + (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(MessageID) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if ( + ( + (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_1 || + (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_2 || + (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_1 || + (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_2 || + (unsigned char)data[0] == ID_CONNECTION_ATTEMPT_FAILED || + (unsigned char)data[0] == ID_NO_FREE_INCOMING_CONNECTIONS || + (unsigned char)data[0] == ID_CONNECTION_BANNED || + (unsigned char)data[0] == ID_ALREADY_CONNECTED || + (unsigned char)data[0] == ID_IP_RECENTLY_CONNECTED) && + (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(MessageID), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if (((unsigned char)data[0] == ID_INCOMPATIBLE_PROTOCOL_VERSION&& + (size_t) length == sizeof(MessageID)*2 + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID))) + { + *isOfflineMessage=memcmp(data+sizeof(MessageID)*2, OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else + { + *isOfflineMessage=false; + } + + if (*isOfflineMessage) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + // These are all messages from unconnected systems. Messages here can be any size, but are never processed from connected systems. + if ( ( (unsigned char) data[ 0 ] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS + || (unsigned char)(data)[0] == ID_UNCONNECTED_PING) && (size_t) length >= sizeof(unsigned char)+sizeof(MafiaNet::Time)+sizeof(OFFLINE_MESSAGE_DATA_ID) ) + { + if ( (unsigned char)(data)[0] == ID_UNCONNECTED_PING || + rakPeer->AllowIncomingConnections() ) // Open connections with players + { + MafiaNet::BitStream inBitStream( (unsigned char *) data, length, false ); + inBitStream.IgnoreBits(8); + MafiaNet::Time sendPingTime; + inBitStream.Read(sendPingTime); + inBitStream.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + RakNetGUID remoteGuid=UNASSIGNED_RAKNET_GUID; + inBitStream.Read(remoteGuid); + + MafiaNet::BitStream outBitStream; + outBitStream.Write((MessageID)ID_UNCONNECTED_PONG); // Should be named ID_UNCONNECTED_PONG eventually + outBitStream.Write(sendPingTime); + outBitStream.Write(rakPeer->myGuid); + outBitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + + rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Lock(); + // They are connected, so append offline ping data + outBitStream.Write( (char*)rakPeer->offlinePingResponse.GetData(), rakPeer->offlinePingResponse.GetNumberOfBytesUsed() ); + rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Unlock(); + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*)outBitStream.GetData(), outBitStream.GetNumberOfBytesUsed(), systemAddress); + + RNS2_SendParameters bsp; + bsp.data = (char*) outBitStream.GetData(); + bsp.length = outBitStream.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + + // SocketLayer::SendTo( rakNetSocket, (const char*)outBitStream.GetData(), (unsigned int) outBitStream.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + + packet=rakPeer->AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); + packet->data[0]=data[0]; + packet->systemAddress = systemAddress; + packet->guid=remoteGuid; + packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + rakPeer->AddPacketToProducer(packet); + } + } + // UNCONNECTED MESSAGE Pong with no data. + else if ((unsigned char) data[ 0 ] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID) && (size_t) length < sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID)+MAX_OFFLINE_DATA_LENGTH) + { + packet=rakPeer->AllocPacket((unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(MafiaNet::Time)+sizeof(MafiaNet::TimeMS)), _FILE_AND_LINE_); + MafiaNet::BitStream bsIn((unsigned char*) data, length, false); + bsIn.IgnoreBytes(sizeof(unsigned char)); + MafiaNet::Time ping; + bsIn.Read(ping); + bsIn.Read(packet->guid); + + MafiaNet::BitStream bsOut((unsigned char*) packet->data, packet->length, false); + bsOut.ResetWritePointer(); + bsOut.Write((unsigned char)ID_UNCONNECTED_PONG); + MafiaNet::TimeMS pingMS=(MafiaNet::TimeMS)ping; + bsOut.Write(pingMS); + bsOut.WriteAlignedBytes( + (const unsigned char*)data+sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID), + length-sizeof(unsigned char)-sizeof(MafiaNet::Time)-RakNetGUID::size()-sizeof(OFFLINE_MESSAGE_DATA_ID) + ); + + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + rakPeer->AddPacketToProducer(packet); + } + else if ((unsigned char) data[ 0 ] == ID_OUT_OF_BAND_INTERNAL && + (size_t) length > sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)+RakNetGUID::size() && + (size_t) length < MAX_OFFLINE_DATA_LENGTH+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)+RakNetGUID::size()) + { + unsigned int dataLength = (unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(MessageID)); + RakAssert(dataLength<1024); + packet=rakPeer->AllocPacket(dataLength+1, _FILE_AND_LINE_); + RakAssert(packet->length<1024); + + MafiaNet::BitStream bs2((unsigned char*) data, length, false); + bs2.IgnoreBytes(sizeof(MessageID)); + bs2.Read(packet->guid); + + if (data[sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size()]==ID_ADVERTISE_SYSTEM) + { + packet->length--; + packet->bitSize=BYTES_TO_BITS(packet->length); + packet->data[0]=ID_ADVERTISE_SYSTEM; + memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)*2 + RakNetGUID::size(), dataLength-1); + } + else + { + packet->data[0]=ID_OUT_OF_BAND_INTERNAL; + memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size(), dataLength); + } + + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + rakPeer->AddPacketToProducer(packet); + } + else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_1) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + MafiaNet::BitStream bsIn((unsigned char*) data,length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + bsIn.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + RakNetGUID serverGuid; + bsIn.Read(serverGuid); + unsigned char serverHasSecurity; + uint32_t cookie; + (void) cookie; + bsIn.Read(serverHasSecurity); + // Even if the server has security, it may not be required of us if we are in the security exception list + if (serverHasSecurity) + { + bsIn.Read(cookie); + } + + MafiaNet::BitStream bsOut; + bsOut.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_2); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + if (serverHasSecurity) + bsOut.Write(cookie); + + rakPeer->requestedConnectionQueueMutex.Lock(); + for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) + { + RakPeer::RequestedConnectionStruct *rcs; + rcs=rakPeer->requestedConnectionQueue[i]; + if (rcs->systemAddress==systemAddress) + { + if (serverHasSecurity) + { +#if LIBCAT_SECURITY==1 + unsigned char public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; + bsIn.ReadAlignedBytes(public_key, sizeof(public_key)); + + if (rcs->publicKeyMode==PKM_ACCEPT_ANY_PUBLIC_KEY) + { + memcpy(rcs->remote_public_key, public_key, cat::EasyHandshake::PUBLIC_KEY_BYTES); + if (!rcs->client_handshake->Initialize(public_key) || + !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) + { + CAT_AUDIT_PRINTF("AUDIT: Server passed a bad public key with PKM_ACCEPT_ANY_PUBLIC_KEY"); + rakPeer->requestedConnectionQueueMutex.Unlock(); + return true; + } + } + + if (cat::SecureEqual(public_key, + rcs->remote_public_key, + cat::EasyHandshake::PUBLIC_KEY_BYTES)==false) + { + rakPeer->requestedConnectionQueueMutex.Unlock(); + CAT_AUDIT_PRINTF("AUDIT: Expected public key does not match what was sent by server -- Reporting back ID_PUBLIC_KEY_MISMATCH to user\n"); + + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_PUBLIC_KEY_MISMATCH; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=serverGuid; + rakPeer->AddPacketToProducer(packet); + return true; + } + + if (rcs->client_handshake==0) + { + // Message does not contain a challenge + // We might still pass if we are in the security exception list + bsOut.Write((unsigned char)0); + } + else + { + // Message contains a challenge + bsOut.Write((unsigned char)1); + // challenge + CAT_AUDIT_PRINTF("AUDIT: Sending challenge\n"); + bsOut.WriteAlignedBytes((const unsigned char*) rcs->handshakeChallenge,cat::EasyHandshake::CHALLENGE_BYTES); + } +#else // LIBCAT_SECURITY + // Message does not contain a challenge + bsOut.Write((unsigned char)0); +#endif // LIBCAT_SECURITY + } + else + { + // Server does not need security +#if LIBCAT_SECURITY==1 + if (rcs->client_handshake!=0) + { + rakPeer->requestedConnectionQueueMutex.Unlock(); + CAT_AUDIT_PRINTF("AUDIT: Security disabled by server but we expected security (indicated by client_handshake not null) so failing!\n"); + + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_OUR_SYSTEM_REQUIRES_SECURITY; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=serverGuid; + rakPeer->AddPacketToProducer(packet); + return true; + } +#endif // LIBCAT_SECURITY + + } + + uint16_t mtu; + bsIn.Read(mtu); + + // Binding address + bsOut.Write(rcs->systemAddress); + rakPeer->requestedConnectionQueueMutex.Unlock(); + // MTU + bsOut.Write(mtu); + // Our guid + bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), rcs->systemAddress); + + // SocketLayer::SendTo( rakPeer->socketList[rcs->socketIndex], (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), rcs->systemAddress, _FILE_AND_LINE_ ); + + RNS2_SendParameters bsp; + bsp.data = (char*) bsOut.GetData(); + bsp.length = bsOut.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + + return true; + } + } + rakPeer->requestedConnectionQueueMutex.Unlock(); + } + else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_2) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + MafiaNet::BitStream bs((unsigned char*) data,length,false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + RakNetGUID guid; + bs.Read(guid); + SystemAddress bindingAddress; + [[maybe_unused]] bool b = bs.Read(bindingAddress); // only checked by RakAssert (debug builds) + RakAssert(b); + uint16_t mtu; + b=bs.Read(mtu); + RakAssert(b); + bool doSecurity=false; + b=bs.Read(doSecurity); + RakAssert(b); + +#if LIBCAT_SECURITY==1 + char answer[cat::EasyHandshake::ANSWER_BYTES]; + CAT_AUDIT_PRINTF("AUDIT: Got ID_OPEN_CONNECTION_REPLY_2 and given doSecurity=%i\n", (int)doSecurity); + if (doSecurity) + { + CAT_AUDIT_PRINTF("AUDIT: Reading cookie and public key\n"); + bs.ReadAlignedBytes((unsigned char*) answer, sizeof(answer)); + } + cat::ClientEasyHandshake *client_handshake=0; +#endif // LIBCAT_SECURITY + + RakPeer::RequestedConnectionStruct *rcs; + bool unlock=true; + rakPeer->requestedConnectionQueueMutex.Lock(); + for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) + { + rcs=rakPeer->requestedConnectionQueue[i]; + + + if (rcs->systemAddress==systemAddress) + { +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: System address matches an entry in the requestedConnectionQueue and doSecurity=%i\n", (int)doSecurity); + if (doSecurity) + { + if (rcs->client_handshake==0) + { + CAT_AUDIT_PRINTF("AUDIT: Server wants security but we didn't set a public key -- Reporting back ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY to user\n"); + rakPeer->requestedConnectionQueueMutex.Unlock(); + + packet=rakPeer->AllocPacket(2, _FILE_AND_LINE_); + packet->data[ 0 ] = ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY; // Attempted a connection and couldn't + packet->data[ 1 ] = 0; // Indicate server public key is missing + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=guid; + rakPeer->AddPacketToProducer(packet); + return true; + } + + CAT_AUDIT_PRINTF("AUDIT: Looks good, preparing to send challenge to server! client_handshake = %x\n", client_handshake); + } + +#endif // LIBCAT_SECURITY + + rakPeer->requestedConnectionQueueMutex.Unlock(); + unlock=false; + + RakAssert(rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT); + // You might get this when already connected because of cross-connections + bool thisIPConnectedRecently=false; + remoteSystem=rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + if (remoteSystem==0) + { + if (rcs->socket == 0) + { + remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); + } + else + { + remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rcs->socket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); + } + } + + // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots + // Ignore connection attempts if this IP address connected within the last 100 milliseconds + if (thisIPConnectedRecently==false) + { + // Don't check GetRemoteSystemFromGUID, server will verify + if (remoteSystem) + { + // Move pointer from RequestedConnectionStruct to RemoteSystemStruct +#if LIBCAT_SECURITY==1 + cat::u8 ident[cat::EasyHandshake::IDENTITY_BYTES]; + bool doIdentity = false; + + if (rcs->client_handshake) + { + CAT_AUDIT_PRINTF("AUDIT: Processing answer\n"); + if (rcs->publicKeyMode == PKM_USE_TWO_WAY_AUTHENTICATION) + { + if (!rcs->client_handshake->ProcessAnswerWithIdentity(answer, ident, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) + { + CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); + return true; + } + + doIdentity = true; + } + else + { + if (!rcs->client_handshake->ProcessAnswer(answer, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) + { + CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); + return true; + } + } + CAT_AUDIT_PRINTF("AUDIT: Success!\n"); + + MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + rcs->client_handshake=0; + } +#endif // LIBCAT_SECURITY + + remoteSystem->weInitiatedTheConnection=true; + remoteSystem->connectMode=RakPeer::RemoteSystemStruct::REQUESTED_CONNECTION; + if (rcs->timeoutTime!=0) + remoteSystem->reliabilityLayer.SetTimeoutTime(rcs->timeoutTime); + + MafiaNet::BitStream temp; + temp.Write( (MessageID)ID_CONNECTION_REQUEST); + temp.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + temp.Write(MafiaNet::GetTime()); + +#if LIBCAT_SECURITY==1 + temp.Write((unsigned char)(doSecurity ? 1 : 0)); + + if (doSecurity) + { + unsigned char proof[32]; + remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->GenerateProof(proof, sizeof(proof)); + temp.WriteAlignedBytes(proof, sizeof(proof)); + + temp.Write((unsigned char)(doIdentity ? 1 : 0)); + + if (doIdentity) + { + temp.WriteAlignedBytes(ident, sizeof(ident)); + } + } +#else + temp.Write((unsigned char)0); +#endif // LIBCAT_SECURITY + + if ( rcs->outgoingPasswordLength > 0 ) + temp.Write( ( char* ) rcs->outgoingPassword, rcs->outgoingPasswordLength ); + + rakPeer->SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, timeRead, 0 ); + } + else + { + // Failed, no connections available anymore + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=guid; + rakPeer->AddPacketToProducer(packet); + } + } + + rakPeer->requestedConnectionQueueMutex.Lock(); + for (unsigned int k=0; k < rakPeer->requestedConnectionQueue.Size(); k++) + { + if (rakPeer->requestedConnectionQueue[k]->systemAddress==systemAddress) + { + rakPeer->requestedConnectionQueue.RemoveAtIndex(k); + break; + } + } + rakPeer->requestedConnectionQueueMutex.Unlock(); + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Deleting client_handshake object %x and rcs->client_handshake object %x\n", client_handshake, rcs->client_handshake); + MafiaNet::OP_DELETE(client_handshake,_FILE_AND_LINE_); + MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); +#endif // LIBCAT_SECURITY + MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); + + break; + } + } + + if (unlock) + rakPeer->requestedConnectionQueueMutex.Unlock(); + + return true; + + } + else if ((unsigned char)(data)[0] == (MessageID)ID_CONNECTION_ATTEMPT_FAILED || + (unsigned char)(data)[0] == (MessageID)ID_NO_FREE_INCOMING_CONNECTIONS || + (unsigned char)(data)[0] == (MessageID)ID_CONNECTION_BANNED || + (unsigned char)(data)[0] == (MessageID)ID_ALREADY_CONNECTED || + (unsigned char)(data)[0] == (MessageID)ID_INVALID_PASSWORD || + (unsigned char)(data)[0] == (MessageID)ID_IP_RECENTLY_CONNECTED || + (unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) + { + + MafiaNet::BitStream bs((unsigned char*) data,length,false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + if ((unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) + bs.IgnoreBytes(sizeof(unsigned char)); + + RakNetGUID guid; + bs.Read(guid); + + RakPeer::RequestedConnectionStruct *rcs; + bool connectionAttemptCancelled=false; + rakPeer->requestedConnectionQueueMutex.Lock(); + for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) + { + rcs=rakPeer->requestedConnectionQueue[i]; + if (rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT && rcs->systemAddress==systemAddress) + { + connectionAttemptCancelled=true; + rakPeer->requestedConnectionQueue.RemoveAtIndex(i); + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Connection attempt canceled so deleting rcs->client_handshake object %x\n", rcs->client_handshake); + MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); +#endif // LIBCAT_SECURITY + MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); + break; + } + } + + rakPeer->requestedConnectionQueueMutex.Unlock(); + + if (connectionAttemptCancelled) + { + // Tell user of connection attempt failed + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = data[0]; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = systemAddress; + packet->guid=guid; + rakPeer->AddPacketToProducer(packet); + } + } + else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_1 && length > (int) (1+sizeof(OFFLINE_MESSAGE_DATA_ID))) + {/* + static int x = 0; + ++x; + + SystemAddress *addr = (SystemAddress*)&systemAddress; + addr->binaryAddress += x;*/ + + //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); + char remoteProtocol=data[1+sizeof(OFFLINE_MESSAGE_DATA_ID)]; + if (remoteProtocol!=RAKNET_PROTOCOL_VERSION) + { + MafiaNet::BitStream bs; + bs.Write((MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION); + bs.Write((unsigned char)RAKNET_PROTOCOL_VERSION); + bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*)bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); + + // SocketLayer::SendTo( rakNetSocket, (char*)bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + + RNS2_SendParameters bsp; + bsp.data = (char*) bs.GetData(); + bsp.length = bs.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + return true; + } + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + MafiaNet::BitStream bsOut; + bsOut.Write((MessageID)ID_OPEN_CONNECTION_REPLY_1); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); +#if LIBCAT_SECURITY==1 + if (rakPeer->_using_security) + { + bsOut.Write((unsigned char) 1); // HasCookie Yes + // Write cookie + uint32_t cookie = rakPeer->_cookie_jar->Generate(&systemAddress.address,sizeof(systemAddress.address)); + CAT_AUDIT_PRINTF("AUDIT: Writing cookie %i to %i:%i\n", cookie, systemAddress); + bsOut.Write(cookie); + // Write my public key + bsOut.WriteAlignedBytes((const unsigned char *) rakPeer->my_public_key,sizeof(rakPeer->my_public_key)); + } + else +#endif // LIBCAT_SECURITY + bsOut.Write((unsigned char) 0); // HasCookie oN + + // MTU. Lower MTU if it is exceeds our own limit + if (length+UDP_HEADER_SIZE > MAXIMUM_MTU_SIZE) + bsOut.WriteCasted(MAXIMUM_MTU_SIZE); + else + bsOut.WriteCasted(length+UDP_HEADER_SIZE); + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + // SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + + RNS2_SendParameters bsp; + bsp.data = (char*) bsOut.GetData(); + bsp.length = bsOut.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + } + else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_2) + { + SystemAddress bindingAddress; + RakNetGUID guid; + MafiaNet::BitStream bsOut; + MafiaNet::BitStream bs((unsigned char*) data, length, false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + + bool requiresSecurityOfThisClient=false; +#if LIBCAT_SECURITY==1 + char remoteHandshakeChallenge[cat::EasyHandshake::CHALLENGE_BYTES]; + + if (rakPeer->_using_security) + { + systemAddress.ToString(false, str1, static_cast(64)); + requiresSecurityOfThisClient=rakPeer->IsInSecurityExceptionList(str1)==false; + + uint32_t cookie; + bs.Read(cookie); + CAT_AUDIT_PRINTF("AUDIT: Got cookie %i from %i:%i\n", cookie, systemAddress); + if (rakPeer->_cookie_jar->Verify(&systemAddress.address,sizeof(systemAddress.address), cookie)==false) + { + return true; + } + CAT_AUDIT_PRINTF("AUDIT: Cookie good!\n"); + + unsigned char clientWroteChallenge; + bs.Read(clientWroteChallenge); + + if (requiresSecurityOfThisClient==true && clientWroteChallenge==0) + { + // Fail, client doesn't support security, and it is required + return true; + } + + if (clientWroteChallenge) + { + bs.ReadAlignedBytes((unsigned char*) remoteHandshakeChallenge, cat::EasyHandshake::CHALLENGE_BYTES); +#ifdef CAT_AUDIT + printf("AUDIT: RECV CHALLENGE "); + for (int ii = 0; ii < sizeof(remoteHandshakeChallenge); ++ii) + { + printf("%02x", (cat::u8)remoteHandshakeChallenge[ii]); + } + printf("\n"); +#endif + } + } +#endif // LIBCAT_SECURITY + + bs.Read(bindingAddress); + uint16_t mtu; + bs.Read(mtu); + bs.Read(guid); + + RakPeer::RemoteSystemStruct *rssFromSA = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + bool IPAddrInUse = rssFromSA != 0 && rssFromSA->isActive; + RakPeer::RemoteSystemStruct *rssFromGuid = rakPeer->GetRemoteSystemFromGUID(guid, true); + bool GUIDInUse = rssFromGuid != 0 && rssFromGuid->isActive; + + // IPAddrInUse, GuidInUse, outcome + // TRUE, , TRUE , ID_OPEN_CONNECTION_REPLY if they are the same, else ID_ALREADY_CONNECTED + // FALSE, , TRUE , ID_ALREADY_CONNECTED (someone else took this guid) + // TRUE, , FALSE , ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) + // FALSE , FALSE , Allow connection + + int outcome; + if (IPAddrInUse & GUIDInUse) + { + if (rssFromSA==rssFromGuid && rssFromSA->connectMode==RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER) + { + // ID_OPEN_CONNECTION_REPLY if they are the same + outcome=1; + + // Note to self: If REQUESTED_CONNECTION, this means two systems attempted to connect to each other at the same time, and one finished first. + // Returns ID)_CONNECTION_REQUEST_ACCEPTED to one system, and ID_ALREADY_CONNECTED followed by ID_NEW_INCOMING_CONNECTION to another + } + else + { + // ID_ALREADY_CONNECTED (restarted raknet, connected again from same ip, plus someone else took this guid) + outcome=2; + } + } + else if (IPAddrInUse==false && GUIDInUse==true) + { + // ID_ALREADY_CONNECTED (someone else took this guid) + outcome=3; + } + else if (IPAddrInUse==true && GUIDInUse==false) + { + // ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) + outcome=4; + } + else + { + // Allow connection + outcome=0; + } + + MafiaNet::BitStream bsAnswer; + bsAnswer.Write((MessageID)ID_OPEN_CONNECTION_REPLY_2); + bsAnswer.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsAnswer.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + bsAnswer.Write(systemAddress); + bsAnswer.Write(mtu); + bsAnswer.Write(requiresSecurityOfThisClient); + + if (outcome==1) + { + // Duplicate connection request packet from packetloss + // Send back the same answer +#if LIBCAT_SECURITY==1 + if (requiresSecurityOfThisClient) + { + CAT_AUDIT_PRINTF("AUDIT: Resending public key and answer from packetloss. Sending ID_OPEN_CONNECTION_REPLY_2\n"); + bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); + } +#endif // LIBCAT_SECURITY + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); + // SocketLayer::SendTo( rakNetSocket, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + + RNS2_SendParameters bsp; + bsp.data = (char*) bsAnswer.GetData(); + bsp.length = bsAnswer.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + + return true; + } + else if (outcome!=0) + { + bsOut.Write((MessageID)ID_ALREADY_CONNECTED); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(rakPeer->myGuid); + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + // SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + RNS2_SendParameters bsp; + bsp.data = (char*) bsOut.GetData(); + bsp.length = bsOut.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + + return true; + } + + if (rakPeer->AllowIncomingConnection(systemAddress)==false) + { + bsOut.Write((MessageID)ID_NO_FREE_INCOMING_CONNECTIONS); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(rakPeer->myGuid); + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + //SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + RNS2_SendParameters bsp; + bsp.data = (char*) bsOut.GetData(); + bsp.length = bsOut.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + + return true; + } + + bool thisIPConnectedRecently=false; + rssFromSA = rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, requiresSecurityOfThisClient); + + if (thisIPConnectedRecently==true) + { + bsOut.Write((MessageID)ID_IP_RECENTLY_CONNECTED); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(rakPeer->myGuid); + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + //SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + + RNS2_SendParameters bsp; + bsp.data = (char*) bsOut.GetData(); + bsp.length = bsOut.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + + return true; + } + +#if LIBCAT_SECURITY==1 + if (requiresSecurityOfThisClient) + { + CAT_AUDIT_PRINTF("AUDIT: Writing public key. Sending ID_OPEN_CONNECTION_REPLY_2\n"); + if (rakPeer->_server_handshake->ProcessChallenge(remoteHandshakeChallenge, rssFromSA->answer, rssFromSA->reliabilityLayer.GetAuthenticatedEncryption() )) + { + CAT_AUDIT_PRINTF("AUDIT: Challenge good!\n"); + // Keep going to OK block + } + else + { + CAT_AUDIT_PRINTF("AUDIT: Challenge BAD!\n"); + + // Unassign this remote system + rakPeer->DereferenceRemoteSystem(systemAddress); + return true; + } + + bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); + } +#endif // LIBCAT_SECURITY + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); + // SocketLayer::SendTo( rakNetSocket, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + RNS2_SendParameters bsp; + bsp.data = (char*) bsAnswer.GetData(); + bsp.length = bsAnswer.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + } + return true; + } + + return false; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// #med - deprecate this overload --- socketList[0] is not really ensured to be the correctly bound socket at all! +void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, MafiaNet::TimeUS timeRead, BitStream &updateBitStream ) +{ + ProcessNetworkPacket(systemAddress,data,length,rakPeer,rakPeer->socketList[0],timeRead, updateBitStream); +} +void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSocket2* rakNetSocket, MafiaNet::TimeUS timeRead, BitStream &updateBitStream ) +{ +#if LIBCAT_SECURITY==1 +#ifdef CAT_AUDIT + printf("AUDIT: RECV "); + for (int ii = 0; ii < length; ++ii) + { + printf("%02x", (cat::u8)data[ii]); + } + printf("\n"); +#endif +#endif // LIBCAT_SECURITY + + RakAssert(systemAddress.GetPort()); + bool isOfflineMessage; + if (ProcessOfflineNetworkPacket(systemAddress, data, length, rakPeer, rakNetSocket, &isOfflineMessage, timeRead)) + { + return; + } + +// MafiaNet::Packet *packet; + RakPeer::RemoteSystemStruct *remoteSystem; + + // See if this datagram came from a connected system + remoteSystem = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + if ( remoteSystem ) + { + // Handle regular incoming data + // HandleSocketReceiveFromConnectedPlayer is only safe to be called from the same thread as Update, which is this thread + if ( isOfflineMessage==false) + { + remoteSystem->reliabilityLayer.HandleSocketReceiveFromConnectedPlayer( + data, length, systemAddress, rakPeer->pluginListNTS, remoteSystem->MTUSize, + rakNetSocket, &rnr, timeRead, updateBitStream); + } + } + else + { + // int a=5; + // printf("--- Packet from unknown system %s\n", systemAddress.ToString()); + } +} + +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GenerateSeedFromGuid(void) +{ + /* + // Construct a random seed based on the initial guid value, and the last digits of the difference to each subsequent number + // This assumes that only the last 3 bits of each guidId integer has a meaningful amount of randomness between it and the prior number + unsigned int t = guid.g[0]; + unsigned int i; + for (i=1; i < sizeof(guid.g) / sizeof(guid.g[0]); i++) + { + unsigned int diff = guid.g[i]-guid.g[i-1]; + unsigned int diff3Bits = diff & 0x0007; + diff3Bits <<= 29; + diff3Bits >>= (i-1)*3; + t ^= diff3Bits; + } + + return t; + */ + return (unsigned int) ((myGuid.g >> 32) ^ myGuid.g); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DerefAllSockets(void) +{ + unsigned int i; + for (i=0; i < socketList.Size(); i++) + { + if (socketFactory && !socketList[i]->IsBerkleySocket()) + socketFactory->Destroy(socketList[i]); + else + MafiaNet::OP_DELETE(socketList[i], _FILE_AND_LINE_); + } + socketList.Clear(false, _FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetRakNetSocketFromUserConnectionSocketIndex(unsigned int userIndex) const +{ + unsigned int i; + for (i=0; i < socketList.Size(); i++) + { + if (socketList[i]->GetUserConnectionSocketIndex()==userIndex) + return i; + } + RakAssert("GetRakNetSocketFromUserConnectionSocketIndex failed" && 0); + return (unsigned int) -1; +} + +/* +// DS_APR +void RakPeer::ProcessChromePacket(RakNetSocket2 *s, const char *buffer, int dataSize, const SystemAddress& recvFromAddress, MafiaNet::TimeUS timeRead) +{ + RakAssert(buffer); + RakAssert(dataSize > 0); + RakAssert(recvFromAddress.GetPort()); + + RNS2RecvStruct *recvFromStruct; + recvFromStruct=bufferedPackets.Allocate( _FILE_AND_LINE_ ); + RakAssert(dataSize <= (int)sizeof(recvFromStruct->data)); + memcpy(recvFromStruct->data, buffer, dataSize); + recvFromStruct->bytesRead=dataSize; + recvFromStruct->systemAddress=recvFromAddress; + recvFromStruct->timeRead=timeRead; + bufferedPackets.Push(recvFromStruct); +} +*/ + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +/* +bool RakPeer::RunRecvFromOnce( RakNetSocket *s ) +{ + RakPeer::RecvFromStruct *recvFromStruct; + + recvFromStruct=bufferedPackets.Allocate( _FILE_AND_LINE_ ); + if (recvFromStruct != nullptr) + { + recvFromStruct->s=s; + SocketLayer::RecvFromBlocking(s, this, recvFromStruct->data, &recvFromStruct->bytesRead, &recvFromStruct->systemAddress, &recvFromStruct->timeRead); + + if (recvFromStruct->bytesRead>0) + { + RakAssert(recvFromStruct->systemAddress.GetPort()); + bufferedPackets.Push(recvFromStruct); + quitAndDataEvents.SetEvent(); + + // Got data + return true; + } + else + { + bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); + } + } + // No data + return false; +} +*/ +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::RunUpdateCycle(BitStream &updateBitStream ) +{ + RakPeer::RemoteSystemStruct * remoteSystem; + unsigned int activeSystemListIndex; + Packet *packet; + // int currentSentBytes,currentReceivedBytes; +// unsigned numberOfBytesUsed; +// BitSize_t numberOfBitsUsed; + //SystemAddress authoritativeClientSystemAddress; + BitSize_t bitSize; + unsigned int byteSize; + unsigned char *data; + SystemAddress systemAddress; + BufferedCommandStruct *bcs; + bool callerDataAllocationUsed; + RakNetStatistics *rnss; + MafiaNet::TimeUS timeNS=0; + MafiaNet::Time timeMS=0; + + // This is here so RecvFromBlocking actually gets data from the same thread + +#if defined(_WIN32) + // #high - review this - this is hardly correct to only work with the first socket here... + if (socketList[0]->GetSocketType()==RNS2T_WINDOWS && ((RNS2_Windows*)socketList[0])->GetSocketLayerOverride()) + { + int len; + SystemAddress sender; + char dataOut[ MAXIMUM_MTU_SIZE ]; + do { + len = ((RNS2_Windows*)socketList[0])->GetSocketLayerOverride()->RakNetRecvFrom(dataOut,&sender,true); + if (len>0) + ProcessNetworkPacket( sender, dataOut, len, this, socketList[0], MafiaNet::GetTimeUS(), updateBitStream ); + } while (len>0); + } +#endif + +// unsigned int socketListIndex; + RNS2RecvStruct *recvFromStruct; + while ((recvFromStruct=PopBufferedPacket())!=0) + { + /* + for (socketListIndex=0; socketListIndex < socketList.Size(); socketListIndex++) + { + if ((RakNetSocket*) socketList[socketListIndex]==recvFromStruct->s) + break; + } + if (socketListIndex!=socketList.Size()) + */ + ProcessNetworkPacket(recvFromStruct->systemAddress, recvFromStruct->data, recvFromStruct->bytesRead, this, recvFromStruct->socket, recvFromStruct->timeRead, updateBitStream); + DeallocRNS2RecvStruct(recvFromStruct, _FILE_AND_LINE_); + } + + while ((bcs=bufferedCommands.PopInaccurate())!=0) + { + if (bcs->command==BufferedCommandStruct::BCS_SEND) + { + // GetTime is a very slow call so do it once and as late as possible + if (timeNS==0) + { + timeNS = MafiaNet::GetTimeUS(); + timeMS = (MafiaNet::TimeMS)(timeNS/(MafiaNet::TimeUS)1000); + } + + callerDataAllocationUsed=SendImmediate((char*)bcs->data, bcs->numberOfBitsToSend, bcs->priority, bcs->reliability, bcs->orderingChannel, bcs->systemIdentifier, bcs->broadcast, true, timeNS, bcs->receipt); + if ( callerDataAllocationUsed==false ) + rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); + + // Set the new connection state AFTER we call sendImmediate in case we are setting it to a disconnection state, which does not allow further sends + if (bcs->connectionMode!=RemoteSystemStruct::NO_ACTION ) + { + remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); + if (remoteSystem) + remoteSystem->connectMode=bcs->connectionMode; + } + } + else if (bcs->command==BufferedCommandStruct::BCS_SESSION_ACCEPT || bcs->command==BufferedCommandStruct::BCS_SESSION_REJECT) + { + // Validate here rather than in the user-thread API: only the network thread may read connectMode + // and the awaiting flag without racing the receive path. + RemoteSystemStruct *sessionSystem = GetRemoteSystem( bcs->systemIdentifier, true, true ); + // connectMode is checked as well as the awaiting flag: a peer that disconnects while the decision + // is queued leaves the flag set (the teardown path does not clear session state), and answering + // then would send on a dying connection and report a connection for a peer that has already gone. + if (sessionSystem && sessionSystem->isActive && + sessionSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && + sessionSystem->sessionConfigAwaitingLocalDecision) + { + sessionSystem->sessionConfigAwaitingLocalDecision = false; + const unsigned int sessionPayloadLength = (unsigned int) BITS_TO_BYTES(bcs->numberOfBitsToSend); + if (bcs->command==BufferedCommandStruct::BCS_SESSION_ACCEPT) + { + // With a pending pool the handshake may have started while the server was full, so the + // incoming limit is enforced here. The application should check first; this is the backstop + // that keeps two accepts racing one free slot from both getting it. + SessionAdmission::Limits limits; + limits.maxIncoming = GetMaximumIncomingConnections(); + limits.maxPending = maximumPendingSessions; + limits.maxPendingPerAddress = maximumPendingSessionsPerAddress; + SessionAdmission::Counts counts; + CountRemoteInitiatedPeers(UNASSIGNED_SYSTEM_ADDRESS, counts.connected, counts.exchanging, counts.pending, counts.pendingFromAddress); + if (SessionAdmission::MayAccept(limits, counts)) + SendSessionConfigResponse(sessionSystem, bcs->data, sessionPayloadLength); + else + SendSessionConfigRejection(sessionSystem, 0, 0); + } + else + SendSessionConfigRejection(sessionSystem, bcs->data, sessionPayloadLength); + } + if (bcs->data) + rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); + } + else if (bcs->command==BufferedCommandStruct::BCS_SESSION_STATUS) + { + // Only a server peer still awaiting its application's decision may be told anything, and telling + // it restarts the session timer here as well as on the client when it arrives. + RemoteSystemStruct *sessionSystem = GetRemoteSystem( bcs->systemIdentifier, true, true ); + if (sessionSystem && sessionSystem->isActive && + sessionSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && + sessionSystem->sessionConfigAwaitingLocalDecision && + sessionSystem->sessionConfigIsConnectingSide==false) + { + MafiaNet::BitStream statusStream; + statusStream.Write((MessageID)ID_SESSION_CONFIG_STATUS); + const unsigned int statusLength = (unsigned int) BITS_TO_BYTES(bcs->numberOfBitsToSend); + if (bcs->data && statusLength > 0) + statusStream.Write(bcs->data, statusLength); + SendImmediate((char*)statusStream.GetData(), statusStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, sessionSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); + sessionSystem->sessionConfigStartTime = MafiaNet::GetTimeMS(); + } + if (bcs->data) + rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); + } + else if (bcs->command==BufferedCommandStruct::BCS_CLOSE_CONNECTION) + { + // bcs->socket may be null: the socket can be released between queuing this + // command and processing it here (e.g. during rapid connect/disconnect churn). + // RakAssert is a no-op in release (NDEBUG), so guard explicitly and fall back + // to the primary socket to avoid dereferencing null in CloseConnectionInternal2. + RakNetSocket2 *closeSocket = bcs->socket; + if (closeSocket == nullptr && socketList.Size() > 0) + closeSocket = socketList[0]; + if (closeSocket != nullptr) + CloseConnectionInternal2(bcs->systemIdentifier, false, true, bcs->orderingChannel, bcs->priority, *closeSocket); + } + else if (bcs->command==BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS) + { + // Reroute + RakPeer::RemoteSystemStruct *rssFromGuid = GetRemoteSystem(bcs->systemIdentifier.rakNetGuid,true,true); + if (rssFromGuid!=0) + { + unsigned int existingSystemIndex = GetRemoteSystemIndex(rssFromGuid->systemAddress); + ReferenceRemoteSystem(bcs->systemIdentifier.systemAddress, existingSystemIndex); + } + } + else if (bcs->command==BufferedCommandStruct::BCS_GET_SOCKET) + { + SocketQueryOutput *sqo; + if (bcs->systemIdentifier.IsUndefined()) + { + sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); + sqo->sockets=socketList; + socketQueryOutput.Push(sqo); + } + else + { + remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); + sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); + + sqo->sockets.Clear(false, _FILE_AND_LINE_); + if (remoteSystem) + { + sqo->sockets.Push(remoteSystem->rakNetSocket, _FILE_AND_LINE_ ); + } + else + { + // Leave empty smart pointer + } + socketQueryOutput.Push(sqo); + } + + } + +#ifdef _DEBUG + bcs->data=0; +#endif + + bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); + } + + if (requestedConnectionQueue.IsEmpty()==false) + { + if (timeNS==0) + { + timeNS = MafiaNet::GetTimeUS(); + timeMS = (MafiaNet::TimeMS)(timeNS/(MafiaNet::TimeUS)1000); + } + + bool condition1, condition2; + unsigned requestedConnectionQueueIndex=0; + requestedConnectionQueueMutex.Lock(); + while (requestedConnectionQueueIndex < requestedConnectionQueue.Size()) + { + RequestedConnectionStruct *rcs; + rcs = requestedConnectionQueue[requestedConnectionQueueIndex]; + requestedConnectionQueueMutex.Unlock(); + if (rcs->nextRequestTime < timeMS) + { + condition1=rcs->requestsMade==rcs->sendConnectionAttemptCount+1; + condition2=(bool)((rcs->systemAddress==UNASSIGNED_SYSTEM_ADDRESS)==1); + // If too many requests made or a hole then remove this if possible, otherwise invalidate it + if (condition1 || condition2) + { + if (rcs->data) + { + rakFree_Ex(rcs->data, _FILE_AND_LINE_ ); + rcs->data=0; + } + + if (condition1 && !condition2 && rcs->actionToTake==RequestedConnectionStruct::CONNECT) + { + // Tell user of connection attempt failed + packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + AddPacketToProducer(packet); + } + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Connection attempt FAILED so deleting rcs->client_handshake object %x\n", rcs->client_handshake); + MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); +#endif + MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); + + requestedConnectionQueueMutex.Lock(); + for (unsigned int k=0; k < requestedConnectionQueue.Size(); k++) + { + if (requestedConnectionQueue[k]==rcs) + { + requestedConnectionQueue.RemoveAtIndex(k); + break; + } + } + requestedConnectionQueueMutex.Unlock(); + } + else + { + + const unsigned attemptsPerMTU = GetConnectionAttemptsPerMTU(rcs->sendConnectionAttemptCount); + int MTUSizeIndex = rcs->requestsMade / attemptsPerMTU; + if (MTUSizeIndex>=NUM_MTU_SIZES) + MTUSizeIndex=NUM_MTU_SIZES-1; + rcs->requestsMade++; + rcs->nextRequestTime=timeMS+rcs->timeBetweenSendConnectionAttemptsMS; + + MafiaNet::BitStream bitStream; + //WriteOutOfBandHeader(&bitStream, ID_USER_PACKET_ENUM); + bitStream.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_1); + bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bitStream.Write((MessageID)RAKNET_PROTOCOL_VERSION); + bitStream.PadWithZeroToByteLength(mtuSizes[MTUSizeIndex]-UDP_HEADER_SIZE); + + char str[256]; + rcs->systemAddress.ToString(true,str,static_cast(256)); + + //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); + + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*) bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), rcs->systemAddress); + + RakNetSocket2 *socketToUse; + if (rcs->socket == 0) + socketToUse = socketList[rcs->socketIndex]; + else + socketToUse = rcs->socket; + + rcs->systemAddress.FixForIPVersion(socketToUse->GetBoundAddress()); + if (socketToUse->IsBerkleySocket()) + ((RNS2_Berkley*)socketToUse)->SetDoNotFragment(1); + +// SocketLayer::SetDoNotFragment(socketToUse, 1); + MafiaNet::Time sendToStart= MafiaNet::GetTime(); + + RNS2_SendParameters bsp; + bsp.data = (char*) bitStream.GetData(); + bsp.length = bitStream.GetNumberOfBytesUsed(); + bsp.systemAddress = rcs->systemAddress; + const RNS2SendResult sendResult = socketToUse->Send(&bsp, _FILE_AND_LINE_); + // Send() reports failure as sendto's own return value and drops the reason, so + // the error has to be read back off the thread before anything else touches a + // socket. The old test here compared that return value against 10040 directly, + // which sendto never yields -- it yields SOCKET_ERROR -- so this branch was dead + // and a locally-refused MTU burned its rung's entire attempt budget. + if (sendResult < 0 && RNS2_IsDatagramTooLargeError(RNS2_GetLastSocketError())) + { + // The local interface refused the datagram outright. Every attempt at this + // size fails the same way, so step down now. + rcs->requestsMade = (unsigned char) ((MTUSizeIndex + 1) * attemptsPerMTU); + rcs->nextRequestTime=timeMS; + } + else + { + MafiaNet::Time sendToEnd= MafiaNet::GetTime(); + if (sendToEnd-sendToStart>100) + { + // A send that blocks this long means the outgoing interface is + // struggling: a virtual VPN adapter renegotiating its tunnel, or a full + // transmit queue. Smaller datagrams are likelier to get through, so drop + // to the lowest MTU -- but keep trying. Giving up here (which is what + // this did when already on the lowest rung) turned a transient stall + // into ID_CONNECTION_ATTEMPT_FAILED with most of the budget unspent. + const int lowestMtuIndex = (int) (attemptsPerMTU * (unsigned) (NUM_MTU_SIZES - 1)); + if (lowestMtuIndex > (int) rcs->requestsMade) + { + rcs->requestsMade = (unsigned char) lowestMtuIndex; + rcs->nextRequestTime=timeMS; + } + } + } + // SocketLayer::SetDoNotFragment(socketToUse, 0); + if (socketToUse->IsBerkleySocket()) + ((RNS2_Berkley*)socketToUse)->SetDoNotFragment(0); + + requestedConnectionQueueIndex++; + } + } + else + requestedConnectionQueueIndex++; + + requestedConnectionQueueMutex.Lock(); + } + requestedConnectionQueueMutex.Unlock(); + } + + // remoteSystemList in network thread + for ( activeSystemListIndex = 0; activeSystemListIndex < activeSystemListSize; ++activeSystemListIndex ) + //for ( remoteSystemIndex = 0; remoteSystemIndex < remoteSystemListSize; ++remoteSystemIndex ) + { + // I'm using systemAddress from remoteSystemList but am not locking it because this loop is called very frequently and it doesn't + // matter if we miss or do an extra update. The reliability layers themselves never care which player they are associated with + //systemAddress = remoteSystemList[ remoteSystemIndex ].systemAddress; + // Allow the systemAddress for this remote system list to change. We don't care if it changes now. + // remoteSystemList[ remoteSystemIndex ].allowSystemAddressAssigment=true; + + + // Found an active remote system + remoteSystem = activeSystemList[ activeSystemListIndex ]; + systemAddress = remoteSystem->systemAddress; + RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); + // Update is only safe to call from the same thread that calls HandleSocketReceiveFromConnectedPlayer, + // which is this thread + + if (timeNS==0) + { + timeNS = MafiaNet::GetTimeUS(); + timeMS = (MafiaNet::TimeMS)(timeNS/(MafiaNet::TimeUS)1000); + //RAKNET_DEBUG_PRINTF("timeNS = %I64i timeMS=%i\n", timeNS, timeMS); + } + + + if (timeMS > remoteSystem->lastReliableSend && timeMS-remoteSystem->lastReliableSend > remoteSystem->reliabilityLayer.GetTimeoutTime()/2 && remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) + { + // If no reliable packets are waiting for an ack, do a one byte reliable send so that disconnections are noticed + RakNetStatistics rakNetStatistics; + rnss=remoteSystem->reliabilityLayer.GetStatistics(&rakNetStatistics); + if (rnss->messagesInResendBuffer==0) + { + PingInternal( systemAddress, true, MafiaNet::Reliability::Reliable ); + + //remoteSystem->lastReliableSend=timeMS+remoteSystem->reliabilityLayer.GetTimeoutTime(); + remoteSystem->lastReliableSend=timeMS; + } + } + + if (endThreads) + // for the final call, make sure we send out any outstanding ACKs + remoteSystem->reliabilityLayer.UpdateAndForceACKs( remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream ); // systemAddress only used for the internet simulator test + else + remoteSystem->reliabilityLayer.Update( remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream ); // systemAddress only used for the internet simulator test + + // The reliability layer steps the negotiated MTU down when it + // detects an in-session path-MTU black hole; keep the value + // GetMTUSize() reports in sync with what is actually on the wire. + // Written only on an actual step-down: GetMTUSize() reads this + // field from the user thread without synchronization (as it always + // has for the connect-time write), so avoid turning it into a + // continuously-written field. + const int reliabilityMtu = remoteSystem->reliabilityLayer.GetCurrentMtuBytes(); + if (remoteSystem->MTUSize != reliabilityMtu) + remoteSystem->MTUSize = reliabilityMtu; + + // Check for failure conditions + if ( remoteSystem->reliabilityLayer.IsDeadConnection() || + ((remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY) && remoteSystem->reliabilityLayer.IsOutgoingDataWaiting()==false) || + (remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK && (remoteSystem->reliabilityLayer.AreAcksWaiting()==false || remoteSystem->reliabilityLayer.AckTimeout(timeMS)==true)) || + (( + (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || + remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || + remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) + && timeMS > remoteSystem->connectionTime && timeMS - remoteSystem->connectionTime > 10000)) + ) + { + // RAKNET_DEBUG_PRINTF("timeMS=%i remoteSystem->connectionTime=%i\n", timeMS, remoteSystem->connectionTime ); + + // Before the close notification logic: a held session request is not a reported connection, but + // the application is holding a decision for it and has to learn it will never be needed. + ProduceSessionAbandoned(remoteSystem); + + // Failed. Inform the user? + // TODO - RakNet 4.0 - Return a different message identifier for DISCONNECT_ASAP_SILENTLY and DISCONNECT_ASAP than for DISCONNECT_ON_NO_ACK + // The first two mean we called CloseConnection(), the last means the other system sent us ID_DISCONNECTION_NOTIFICATION + // A connection the application was never told about must not produce a close notification for + // it. On the accepting side that covers a peer refused by RejectSession() (which parks the slot + // in DISCONNECT_ON_NO_ACK so the refusal is delivered first) and one dropped mid-handshake. + // The connecting side is exempt: it has an outstanding Connect() to resolve either way. + const bool reportableConnection = remoteSystem->connectionReportedToApplication || remoteSystem->weInitiatedTheConnection; + if (reportableConnection && + (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED || remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION + || remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA + || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK)) + { + +// MafiaNet::BitStream undeliveredMessages; +// remoteSystem->reliabilityLayer.GetUndeliveredMessages(&undeliveredMessages,remoteSystem->MTUSize); + +// packet=AllocPacket(sizeof( char ) + undeliveredMessages.GetNumberOfBytesUsed()); + // A graceful remote disconnect (DISCONNECT_ON_NO_ACK) may carry a reason payload stashed when the + // notification arrived. Only ID_DISCONNECTION_NOTIFICATION carries it; ID_CONNECTION_LOST and + // ID_CONNECTION_ATTEMPT_FAILED are locally synthesized and stay payload-less. + const bool attachReason = remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK && remoteSystem->disconnectReasonData!=0; + const unsigned int reasonLength = attachReason ? remoteSystem->disconnectReasonLength : 0; + packet=AllocPacket(sizeof( char ) + reasonLength, _FILE_AND_LINE_); + if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) + packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't + else if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA) + // Dropped mid-session-handshake. The connecting side still has an attempt outstanding; the + // accepting side never reported anything, so a lost connection is all it can report. + packet->data[ 0 ] = remoteSystem->sessionConfigIsConnectingSide + ? (unsigned char)ID_CONNECTION_ATTEMPT_FAILED : (unsigned char)ID_CONNECTION_LOST; + else if (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) + packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection + else + packet->data[ 0 ] = ID_DISCONNECTION_NOTIFICATION; // DeadConnection + + if (attachReason) + memcpy(packet->data + sizeof(unsigned char), remoteSystem->disconnectReasonData, reasonLength); + +// memcpy(packet->data+1, undeliveredMessages.GetData(), undeliveredMessages.GetNumberOfBytesUsed()); + + packet->guid = remoteSystem->guid; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + + AddPacketToProducer(packet); + } + // else connection shutting down, don't bother telling the user + +#ifdef _DO_PRINTF + RAKNET_DEBUG_PRINTF("Connection dropped for player %i:%i\n", systemAddress); +#endif + // we are about to close the connection to the remote system, we'd still make sure to send any outstanding ACKs, so for the remote system not unnecessarily waiting for these until its timeout + // (and trying to resend them unnecessarily) + remoteSystem->reliabilityLayer.UpdateAndForceACKs(remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream); + + CloseConnectionInternal2(systemAddress, false, true, 0, MafiaNet::Priority::Low, *(remoteSystem->rakNetSocket)); + continue; + } + + // A session handshake that never completes must not pin the slot. Acks keep flowing in + // EXCHANGING_SESSION_DATA, so the ordinary dead-connection detection above would never fire; bound + // it by the same timeout instead. Interactive servers put an application in the middle of this + // exchange, so it is a reachable state, not a theoretical one. + if ( remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && + remoteSystem->sessionConfigStartTime!=0 && + timeMS > remoteSystem->sessionConfigStartTime && + timeMS - remoteSystem->sessionConfigStartTime > GetSessionTimeoutFor(remoteSystem) ) + { + // Only the connecting side reports a failure; a server just drops the half-open slot. + if (remoteSystem->sessionConfigIsConnectingSide) + { + Packet *timeoutPacket=AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); + timeoutPacket->data[0]=(unsigned char)ID_CONNECTION_ATTEMPT_FAILED; + timeoutPacket->bitSize=sizeof(MessageID)*8; + timeoutPacket->systemAddress=remoteSystem->systemAddress; + timeoutPacket->systemAddress.systemIndex=remoteSystem->remoteSystemIndex; + timeoutPacket->guid=remoteSystem->guid; + timeoutPacket->guid.systemIndex=timeoutPacket->systemAddress.systemIndex; + AddPacketToProducer(timeoutPacket); + } + else + { + ProduceSessionAbandoned(remoteSystem); + } + + remoteSystem->reliabilityLayer.UpdateAndForceACKs(remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream); + CloseConnectionInternal2(systemAddress, false, true, 0, MafiaNet::Priority::Low, *(remoteSystem->rakNetSocket)); + continue; + } + + // Ping this guy if it is time to do so + if ( remoteSystem->connectMode==RemoteSystemStruct::CONNECTED && timeMS > remoteSystem->nextPingTime && ( occasionalPing || remoteSystem->lowestPing == (unsigned short)-1 ) ) + { + remoteSystem->nextPingTime = timeMS + 5000; + PingInternal( systemAddress, true, MafiaNet::Reliability::Unreliable ); + + // Update again immediately after this tick so the ping goes out right away + quitAndDataEvents.SetEvent(); + } + + // Find whoever has the lowest player ID + //if (systemAddress < authoritativeClientSystemAddress) + // authoritativeClientSystemAddress=systemAddress; + + // Does the reliability layer have any packets waiting for us? + // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer + bitSize = remoteSystem->reliabilityLayer.Receive( &data ); + + while ( bitSize > 0 ) + { + // These types are for internal use and should never arrive from a network packet + if (data[0]==ID_CONNECTION_ATTEMPT_FAILED) + { + RakAssert(0); + bitSize=0; + continue; + } + + // Fast and easy - just use the data that was returned + byteSize = (unsigned int) BITS_TO_BYTES( bitSize ); + + // For unknown senders we only accept a few specific packets + if (remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) + { + if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) + { + ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else + { + CloseConnectionInternal2(systemAddress, false, true, 0, MafiaNet::Priority::Low, *(remoteSystem->rakNetSocket)); +#ifdef _DO_PRINTF + RAKNET_DEBUG_PRINTF("Temporarily banning %i:%i for sending nonsense data\n", systemAddress); +#endif + + char str1[64]; + systemAddress.ToString(false, str1, static_cast(64)); + AddToBanList(str1, remoteSystem->reliabilityLayer.GetTimeoutTime()); + + + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else + { + // However, if we are connected we still take a connection request in case both systems are trying to connect to each other + // at the same time + if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) + { + // 04/27/06 This is wrong. With cross connections, we can both have initiated the connection are in state REQUESTED_CONNECTION + // 04/28/06 Downgrading connections from connected will close the connection due to security at ((remoteSystem->connectMode!=RemoteSystemStruct::CONNECTED && time > remoteSystem->connectionTime && time - remoteSystem->connectionTime > 10000)) + if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) + { + ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); + } + else + { + + MafiaNet::BitStream bs((unsigned char*) data,byteSize,false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + bs.IgnoreBytes(RakNetGUID::size()); + MafiaNet::Time incomingTimestamp; + bs.Read(incomingTimestamp); + + // Got a connection request message from someone we are already connected to. Just reply normally. + // This can happen due to race conditions with the fully connected mesh + OnConnectionRequest( remoteSystem, incomingTimestamp ); + } + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char) data[ 0 ] == ID_NEW_INCOMING_CONNECTION && byteSize > sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(MafiaNet::Time)*2 ) + { + if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) + { + // Transport is up, but the application is not told so until the client's session payload + // arrives in ID_SESSION_CONFIG_REQUEST and we answer it. + remoteSystem->connectMode=RemoteSystemStruct::EXCHANGING_SESSION_DATA; + remoteSystem->sessionConfigIsConnectingSide=false; + remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); + PingInternal( systemAddress, true, MafiaNet::Reliability::Unreliable ); + + // Update again immediately after this tick so the ping goes out right away + quitAndDataEvents.SetEvent(); + + MafiaNet::BitStream inBitStream((unsigned char *) data, byteSize, false); + SystemAddress bsSystemAddress; + + inBitStream.IgnoreBits(8); + inBitStream.Read(bsSystemAddress); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); + + MafiaNet::Time sendPingTime, sendPongTime; + inBitStream.Read(sendPingTime); + inBitStream.Read(sendPongTime); + OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); + + // Overwrite the data in the packet + // NewIncomingConnectionStruct newIncomingConnectionStruct; + // MafiaNet::BitStream nICS_BS( data, NewIncomingConnectionStruct_Size, false ); + // newIncomingConnectionStruct.Deserialize( nICS_BS ); + + remoteSystem->myExternalSystemAddress = bsSystemAddress; + + // Bug: If A connects to B through R, A's firstExternalID is set to R. If A tries to send to R, sends to loopback because R==firstExternalID + // Correct fix is to specify in Connect() if target is through a proxy. + // However, in practice you have to connect to something else first anyway to know about the proxy. So setting once only is good enough + if (firstExternalID==UNASSIGNED_SYSTEM_ADDRESS) + { + firstExternalID=bsSystemAddress; + firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); + } + + // Hold this until the session handshake completes; the bytes are copied because `data` + // is freed as soon as this branch returns. + ClearSessionConfig(remoteSystem); + remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); + remoteSystem->withheldConnectionPacketData = (unsigned char*) rakMalloc_Ex(byteSize, _FILE_AND_LINE_); + if (remoteSystem->withheldConnectionPacketData!=0) + { + memcpy(remoteSystem->withheldConnectionPacketData, data, byteSize); + remoteSystem->withheldConnectionPacketLength = byteSize; + } + // AllocPacket used to take ownership of `data` here; the copy above means this branch does. + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else + { + // Send to game even if already connected. This could happen when connecting to 127.0.0.1 + // Ignore, already connected + // rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else if ( (unsigned char) data[ 0 ] == ID_CONNECTED_PONG && byteSize == sizeof(unsigned char)+sizeof(MafiaNet::Time)*2 ) + { + MafiaNet::Time sendPingTime, sendPongTime; + + // Copy into the ping times array the current time - the value returned + // First extract the sent ping + MafiaNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); + //PingStruct ps; + //ps.Deserialize(psBS); + inBitStream.IgnoreBits(8); + inBitStream.Read(sendPingTime); + inBitStream.Read(sendPongTime); + + OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); + + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char)data[0] == ID_CONNECTED_PING && byteSize == sizeof(unsigned char)+sizeof(MafiaNet::Time) ) + { + MafiaNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); + inBitStream.IgnoreBits(8); + MafiaNet::Time sendPingTime; + inBitStream.Read(sendPingTime); + + MafiaNet::BitStream outBitStream; + outBitStream.Write((MessageID)ID_CONNECTED_PONG); + outBitStream.Write(sendPingTime); + outBitStream.Write(MafiaNet::GetTime()); + SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Unreliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0 ); + + // Update again immediately after this tick so the ping goes out right away + quitAndDataEvents.SetEvent(); + + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char) data[ 0 ] == ID_DISCONNECTION_NOTIFICATION ) + { + // Stash any reason payload (everything after the 1-byte ID) so it can ride along with the + // user-facing notification packet synthesized once outstanding ACKs are flushed. This raw + // reliability-layer buffer is freed below, so the bytes have to be copied out now. + ClearDisconnectReason(remoteSystem); + if (byteSize > sizeof(unsigned char)) + { + const unsigned int reasonLength = byteSize - (unsigned int) sizeof(unsigned char); + remoteSystem->disconnectReasonData = (unsigned char*) rakMalloc_Ex(reasonLength, _FILE_AND_LINE_); + if (remoteSystem->disconnectReasonData != 0) + { + memcpy(remoteSystem->disconnectReasonData, data + sizeof(unsigned char), reasonLength); + remoteSystem->disconnectReasonLength = reasonLength; + } + } + + // We shouldn't close the connection immediately because we need to ack the ID_DISCONNECTION_NOTIFICATION + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ON_NO_ACK; + rakFree_Ex(data, _FILE_AND_LINE_ ); + + // AddPacketToProducer(packet); + } + else if ( (unsigned char)(data)[0] == ID_DETECT_LOST_CONNECTIONS && byteSize == sizeof(unsigned char) ) + { + // Do nothing + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG_REQUEST ) + { + // Server side of the session handshake. The client's payload arrives before we answer, so a + // server can inspect it and refuse without ever having disclosed its own. + if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigAwaitingLocalDecision==false) + { + if (StoreRemoteSessionConfig(remoteSystem, (const unsigned char*)data + sizeof(MessageID), byteSize - (unsigned int)sizeof(MessageID))==false) + { + // Oversized payload is a protocol violation; drop without answering. + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if (sessionConfigInteractive) + { + // Let the application decide. The connection stays unreported until it calls + // AcceptSession() or RejectSession(), or the handshake times out below. + remoteSystem->sessionConfigAwaitingLocalDecision=true; + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + } + else + { + rakPeerMutexes[ sessionConfig_Mutex ].Lock(); + SendSessionConfigResponse(remoteSystem, (const char*)sessionConfig.GetData(), (unsigned int)sessionConfig.GetNumberOfBytesUsed()); + rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else + { + // A repeat request, or one on an already-established connection: ignore it rather than + // letting a peer re-run the handshake and re-produce the connection packet. + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG ) + { + // Client side of the session handshake: the server's payload is the last thing we needed, so + // the withheld ID_CONNECTION_REQUEST_ACCEPTED goes out now and the connection becomes real. + // Only the peer that initiated the connection may be answered this way, so a client cannot + // push a payload at a server by replying with the server's own message. + if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigIsConnectingSide) + { + if (StoreRemoteSessionConfig(remoteSystem, (const unsigned char*)data + sizeof(MessageID), byteSize - (unsigned int)sizeof(MessageID))==false) + { + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + } + else + { + remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; + remoteSystem->sessionConfigStartTime=0; + ProduceWithheldConnectionPacket(remoteSystem, (MessageID)ID_CONNECTION_REQUEST_ACCEPTED); + } + } + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG_STATUS ) + { + // Client side, while the server's application decides: surface the status and restart our own + // session timer, which is what lets a queued client wait longer than the timeout. Bound to the + // connecting side like the other replies, so a client cannot push one at a server. + if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigIsConnectingSide) + { + remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + } + else + { + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG_REJECTED ) + { + // Only meaningful to the peer that initiated the connection. Without this a malicious client + // could inject ID_CONNECTION_ATTEMPT_FAILED -- a message the application only ever expects for + // its own outbound connects -- into a listening server's queue. + if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigIsConnectingSide) + { + // Rewrite in place so the reason string the server supplied stays at data+1, matching the + // ID_DISCONNECTION_NOTIFICATION reason convention. No connection is ever reported. + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->data[0]=(unsigned char)ID_CONNECTION_ATTEMPT_FAILED; + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + } + else + { + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else if ( (unsigned char)(data)[0] == ID_INVALID_PASSWORD ) + { + if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) + { + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + } + else + { + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST_ACCEPTED ) + { + if (byteSize > sizeof(MessageID)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(SystemIndex)+sizeof(MafiaNet::Time)*2) + { + // Make sure this connection accept is from someone we wanted to connect to + bool allowConnection, alreadyConnected; + + if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || + remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || + allowConnectionResponseIPMigration) + allowConnection=true; + else + allowConnection=false; + + if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) + alreadyConnected=true; + else + alreadyConnected=false; + + if ( allowConnection ) + { + SystemAddress externalID; + SystemIndex systemIndex; +// SystemAddress internalID; + + MafiaNet::BitStream inBitStream((unsigned char *) data, byteSize, false); + inBitStream.IgnoreBits(8); + // inBitStream.Read(remotePort); + inBitStream.Read(externalID); + inBitStream.Read(systemIndex); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); + + MafiaNet::Time sendPingTime, sendPongTime; + inBitStream.Read(sendPingTime); + inBitStream.Read(sendPongTime); + OnConnectedPong(sendPingTime, sendPongTime, remoteSystem); + + // Find a free remote system struct to use + // MafiaNet::BitStream casBitS(data, byteSize, false); + // ConnectionAcceptStruct cas; + // cas.Deserialize(casBitS); + // systemAddress.GetPort() = remotePort; + + // The remote system told us our external IP, so save it + remoteSystem->myExternalSystemAddress = externalID; + // Transport is up, but the application is not told so until the session payloads have been + // exchanged. ID_CONNECTION_REQUEST_ACCEPTED is stashed and replayed from ID_SESSION_CONFIG. + remoteSystem->connectMode=RemoteSystemStruct::EXCHANGING_SESSION_DATA; + remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); + + // Bug: If A connects to B through R, A's firstExternalID is set to R. If A tries to send to R, sends to loopback because R==firstExternalID + // Correct fix is to specify in Connect() if target is through a proxy. + // However, in practice you have to connect to something else first anyway to know about the proxy. So setting once only is good enough + if (firstExternalID==UNASSIGNED_SYSTEM_ADDRESS) + { + firstExternalID=externalID; + firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); + } + + // Hold the connection-complete packet until the session handshake finishes. The bytes are + // copied because `data` is freed as soon as this branch returns. + ClearSessionConfig(remoteSystem); + remoteSystem->sessionConfigIsConnectingSide=true; + remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); + remoteSystem->withheldConnectionPacketData = (unsigned char*) rakMalloc_Ex(byteSize, _FILE_AND_LINE_); + if (remoteSystem->withheldConnectionPacketData!=0) + { + memcpy(remoteSystem->withheldConnectionPacketData, data, byteSize); + remoteSystem->withheldConnectionPacketLength = byteSize; + } + // AllocPacket used to take ownership of `data` here; the copy above means this branch does. + rakFree_Ex(data, _FILE_AND_LINE_ ); + + MafiaNet::BitStream outBitStream; + outBitStream.Write((MessageID)ID_NEW_INCOMING_CONNECTION); + outBitStream.Write(systemAddress); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + outBitStream.Write(ipList[i]); + outBitStream.Write(sendPongTime); + outBitStream.Write(MafiaNet::GetTime()); + + SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0 ); + + // Our half of the session handshake. Sent on the same ordered channel right behind + // ID_NEW_INCOMING_CONNECTION, so the server has the transport connection before this lands. + { + MafiaNet::BitStream sessionBitStream; + sessionBitStream.Write((MessageID)ID_SESSION_CONFIG_REQUEST); + rakPeerMutexes[ sessionConfig_Mutex ].Lock(); + if (sessionConfig.GetNumberOfBytesUsed() > 0) + sessionBitStream.Write((const char*)sessionConfig.GetData(), sessionConfig.GetNumberOfBytesUsed()); + rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); + SendImmediate( (char*)sessionBitStream.GetData(), sessionBitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0 ); + } + + if (alreadyConnected==false) + { + PingInternal( systemAddress, true, MafiaNet::Reliability::Unreliable ); + } + } + else + { + // Ignore, already connected + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else + { + // Version mismatch error? + RakAssert(0); + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else + { + // What do I do if I get a message from a system, before I am fully connected? + // I can either ignore it or give it to the user + // It seems like giving it to the user is a better option + // + // Except while the session handshake is running: the application has not been told this + // connection exists, so handing it application data from that peer would deliver a packet + // for a guid it has never seen connect -- and from a peer it may still refuse. A peer that + // respects the protocol cannot send here anyway: its own public send path is gated on the + // same state, so anything arriving in this window is malformed or hostile. + if ((data[0]>=(MessageID)ID_TIMESTAMP || data[0]==ID_SND_RECEIPT_ACKED || data[0]==ID_SND_RECEIPT_LOSS) && + remoteSystem->isActive && + remoteSystem->connectMode!=RemoteSystemStruct::EXCHANGING_SESSION_DATA + ) + { + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + if (!DispatchNetworkThreadReceive(packet)) + AddPacketToProducer(packet); + } + else + { + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + } + + // Does the reliability layer have any more packets waiting for us? + // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer + bitSize = remoteSystem->reliabilityLayer.Receive( &data ); + } + + } + + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void RakPeer::OnRNS2Recv(RNS2RecvStruct *recvStruct) +{ + if (incomingDatagramEventHandler) + { + if (incomingDatagramEventHandler(recvStruct)!=true) + return; + } + + PushBufferedPacket(recvStruct); + quitAndDataEvents.SetEvent(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RAK_THREAD_DECLARATION(MafiaNet::UpdateNetworkLoop) +{ + + + + RakPeer * rakPeer = ( RakPeer * ) arguments; + + +/* + // 11/15/05 - this is slower than Sleep() +#ifdef _WIN32 +#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) + // Lets see if these timers give better performance than Sleep + HANDLE timerHandle; + LARGE_INTEGER dueTime; + + if ( rakPeer->threadSleepTimer <= 0 ) + rakPeer->threadSleepTimer = 1; + + // 2nd parameter of false means synchronization timer instead of manual-reset timer + timerHandle = CreateWaitableTimer( nullptr, FALSE, 0 ); + + RakAssert( timerHandle ); + + dueTime.QuadPart = -10000 * rakPeer->threadSleepTimer; // 10000 is 1 ms? + + BOOL success = SetWaitableTimer( timerHandle, &dueTime, rakPeer->threadSleepTimer, nullptr, nullptr, FALSE ); + (void) success; + RakAssert( success ); + +#endif +#endif +*/ + + BitStream updateBitStream( MAXIMUM_MTU_SIZE +#if LIBCAT_SECURITY==1 + + cat::AuthenticatedEncryption::OVERHEAD_BYTES +#endif + ); +// + rakPeer->isMainLoopThreadActive = true; + + bool running = true; + while ( running ) + { + if (rakPeer->endThreads) { + // allow just one more final update-run prior to shutting down this thread to make sure that outstanding acks are still sent and the peers don't unnecessary wait for already retrieved packets + // note: this fixes part of SLNET-123 + running = false; + } +// #ifdef _DEBUG +// // Sanity check, make sure RunUpdateCycle does not block or not otherwise get called for a long time +// RakNetTime thisCall=MafiaNet::GetTime(); +// RakAssert(thisCall-lastCall<250); +// lastCall=thisCall; +// #endif + if (rakPeer->userUpdateThreadPtr) + rakPeer->userUpdateThreadPtr(rakPeer, rakPeer->userUpdateThreadData); + + rakPeer->RunUpdateCycle(updateBitStream); + + // Pending sends go out this often, unless quitAndDataEvents is set + rakPeer->quitAndDataEvents.WaitOnEvent(10); + + /* + +// #if ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && +#if defined(USE_WAIT_FOR_MULTIPLE_EVENTS) && defined(_WIN32) + + if (rakPeer->threadSleepTimer>0) + { + WSAEVENT eventArray[256]; + unsigned int i, eventArrayIndex; + for (i=0,eventArrayIndex=0; i < rakPeer->socketList.Size(); i++) + { + if (rakPeer->socketList[i]->recvEvent!=INVALID_HANDLE_VALUE) + { + eventArray[eventArrayIndex]=rakPeer->socketList[i]->recvEvent; + eventArrayIndex++; + if (eventArrayIndex==256) + break; + } + } + WSAWaitForMultipleEvents(eventArrayIndex,(const HANDLE*) &eventArray,FALSE,rakPeer->threadSleepTimer,FALSE); + } + else + { + RakSleep(0); + } + +#else // ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) + #pragma message("-- RakNet: Using Sleep(). Uncomment USE_WAIT_FOR_MULTIPLE_EVENTS in defines.h if you want to use WaitForSingleObject instead. --") + + RakSleep( rakPeer->threadSleepTimer ); +#endif + */ + } + + rakPeer->isMainLoopThreadActive = false; + + /* +#ifdef _WIN32 +#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) + CloseHandle(timerHandle); +#endif +#endif + */ + + + + + return 0; + +} + +void RakPeer::CallPluginCallbacks(DataStructures::List &pluginList, Packet *packet) +{ + for (unsigned int i=0; i < pluginList.Size(); i++) + { + switch (packet->data[0]) + { + case ID_DISCONNECTION_NOTIFICATION: + pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_DISCONNECTION_NOTIFICATION); + break; + case ID_CONNECTION_LOST: + pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_CONNECTION_LOST); + break; + case ID_NEW_INCOMING_CONNECTION: + pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, true); + break; + case ID_CONNECTION_REQUEST_ACCEPTED: + pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, false); + break; + case ID_CONNECTION_ATTEMPT_FAILED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_ATTEMPT_FAILED); + break; + case ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); + break; + case ID_OUR_SYSTEM_REQUIRES_SECURITY: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_OUR_SYSTEM_REQUIRES_SECURITY); + break; + case ID_PUBLIC_KEY_MISMATCH: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_PUBLIC_KEY_MISMATCH); + break; + case ID_ALREADY_CONNECTED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_ALREADY_CONNECTED); + break; + case ID_NO_FREE_INCOMING_CONNECTIONS: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_NO_FREE_INCOMING_CONNECTIONS); + break; + case ID_CONNECTION_BANNED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_BANNED); + break; + case ID_INVALID_PASSWORD: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INVALID_PASSWORD); + break; + case ID_INCOMPATIBLE_PROTOCOL_VERSION: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INCOMPATIBLE_PROTOCOL); + break; + case ID_IP_RECENTLY_CONNECTED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_IP_RECENTLY_CONNECTED); + break; + } + } +} + +void RakPeer::FillIPList(void) +{ + if (ipList[0]!=UNASSIGNED_SYSTEM_ADDRESS) + return; + + // Fill out ipList structure + RakNetSocket2::GetMyIP( ipList ); + + // Sort the addresses from lowest to highest + int startingIdx = 0; + while (startingIdx < MAXIMUM_NUMBER_OF_INTERNAL_IDS-1 && ipList[startingIdx] != UNASSIGNED_SYSTEM_ADDRESS) + { + int lowestIdx = startingIdx; + for (int curIdx = startingIdx + 1; curIdx < MAXIMUM_NUMBER_OF_INTERNAL_IDS-1 && ipList[curIdx] != UNASSIGNED_SYSTEM_ADDRESS; curIdx++ ) + { + if (ipList[curIdx] < ipList[startingIdx]) + { + lowestIdx = curIdx; + } + } + if (startingIdx != lowestIdx) + { + SystemAddress temp = ipList[startingIdx]; + ipList[startingIdx] = ipList[lowestIdx]; + ipList[lowestIdx] = temp; + } + ++startingIdx; + } +} + + +// #if defined(RMO_NEW_UNDEF_ALLOCATING_QUEUE) +// #pragma pop_macro("new") +// #undef RMO_NEW_UNDEF_ALLOCATING_QUEUE +// #endif + diff --git a/Source/src/voice.cpp b/Source/src/voice.cpp index 18da3ce17..449b7c95a 100644 --- a/Source/src/voice.cpp +++ b/Source/src/voice.cpp @@ -66,6 +66,7 @@ RakVoice::RakVoice() bufferSizeBytes = 0; relayMode = false; relayHost = false; + hasPendingRelay = false; perSpeakerOutput = false; maxDecodedSpeakers = 0; relayTarget = UNASSIGNED_RAKNET_GUID; @@ -162,6 +163,14 @@ void RakVoice::SetRelayHost(std::function relay) { // Swapped under the lock OnReceiveNetworkThread calls it under, so a relay being replaced is // never mid-call when this returns. + if (relayThread.load() == std::this_thread::get_id()) + { + // From inside the running relay, which holds the lock: swap once it returns. + pendingRelay = std::move(relay); + hasPendingRelay = true; + return; + } + relayFrameHandlerMutex.Lock(); relayHost = static_cast(relay); relayFrameHandler = std::move(relay); @@ -769,7 +778,16 @@ PluginReceiveResult RakVoice::OnReceiveNetworkThread(Packet *packet) relayFrameHandlerMutex.Unlock(); return RR_CONTINUE_PROCESSING; } + relayThread = std::this_thread::get_id(); relayFrameHandler(packet); + relayThread = std::thread::id(); + if (hasPendingRelay) + { + relayHost = static_cast(pendingRelay); + relayFrameHandler = std::move(pendingRelay); + pendingRelay = nullptr; + hasPendingRelay = false; + } relayFrameHandlerMutex.Unlock(); return RR_STOP_PROCESSING_AND_DEALLOCATE; } diff --git a/Tests/Integration/NetworkThreadReceiveLiveTests.cpp b/Tests/Integration/NetworkThreadReceiveLiveTests.cpp new file mode 100644 index 000000000..b6cd824d8 --- /dev/null +++ b/Tests/Integration/NetworkThreadReceiveLiveTests.cpp @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2026, MafiaHub + * + * This source code is licensed under the MIT-style license found in the + * license.txt file in the root directory of this source tree. + */ + +// PluginInterface2::OnReceiveNetworkThread against the real threaded update loop: +// the guarantees that only mean something when the hook runs on another thread +// than the one calling DetachPlugin() or Shutdown(). + +#include + +#include "mafianet/bit_stream.h" +#include "mafianet/get_time.h" +#include "mafianet/message_identifiers.h" +#include "mafianet/peer_handle.h" +#include "mafianet/plugin_interface2.h" +#include "mafianet/sleep.h" + +#include +#include +#include + +using namespace MafiaNet; + +namespace +{ + const MessageID ID_LIVE_NT = ID_USER_PACKET_ENUM + 30; + + // Blocks inside the hook until released, so a test can act while it is mid-call. + class BlockingProbe : public PluginInterface2 + { + public: + PluginReceiveResult OnReceiveNetworkThread(Packet *packet) override + { + if (packet->data[0] != ID_LIVE_NT) + return RR_CONTINUE_PROCESSING; + ++calls; + if (block.exchange(false)) + { + entered.set_value(); + release.get_future().wait(); + } + // Lingering inside the call makes it likely one is in flight when Shutdown() starts, + // which is the case the guarantee is about. + if (dwellMs > 0) + RakSleep(dwellMs); + if (shutdownStarted.load()) + ++callsAfterShutdownStarted; + return RR_STOP_PROCESSING_AND_DEALLOCATE; + } + + void OnRakPeerShutdown(void) override { shutdownStarted = true; } + + std::atomic block {false}; + unsigned dwellMs = 0; + std::promise entered; + std::promise release; + std::atomic calls {0}; + std::atomic shutdownStarted {false}; + std::atomic callsAfterShutdownStarted {0}; + }; + + struct LivePair + { + Peer server; + Peer client; + SystemAddress serverAddress; + + bool Start() + { + SocketDescriptor serverSd(0, "127.0.0.1"); + if (server->Startup(1, &serverSd, 1) != RAKNET_STARTED) + return false; + server->SetMaximumIncomingConnections(1); + const unsigned short port = server->GetInternalID().GetPort(); + serverAddress.SetBinaryAddress("127.0.0.1"); + serverAddress.SetPortHostOrder(port); + + RakSleep(2); // distinct microsecond-seeded GUIDs on POSIX + SocketDescriptor clientSd; + if (client->Startup(1, &clientSd, 1) != RAKNET_STARTED || + client->Connect("127.0.0.1", port, 0, 0) != CONNECTION_ATTEMPT_STARTED) + return false; + + const TimeMS start = GetTimeMS(); + while (GetTimeMS() - start < 10000 && + !(server->NumberOfConnections() == 1 && client->GetConnectionState(serverAddress) == IS_CONNECTED)) + { + for (auto pkt : client.incoming()) (void) pkt; + RakSleep(30); + } + return client->GetConnectionState(serverAddress) == IS_CONNECTED; + } + + void Send() + { + BitStream bs; + bs.Write(ID_LIVE_NT); + client->Send(&bs, Priority::High, Reliability::ReliableOrdered, 0, serverAddress, false); + } + }; +} + +class NetworkThreadReceiveLive : public ::testing::Test +{ + // Peers are RAII MafiaNet::Peer handles inside LivePair; each test declares its + // plugin before the pair so the plugin outlives the peer it is attached to. +}; + +TEST_F(NetworkThreadReceiveLive, DetachWaitsForAHookThatIsMidCall) +{ + BlockingProbe probe; + LivePair pair; + ASSERT_TRUE(pair.Start()); + probe.block = true; + pair.server->AttachPlugin(&probe); + std::future entered = probe.entered.get_future(); + + pair.Send(); + ASSERT_EQ(std::future_status::ready, entered.wait_for(std::chrono::seconds(10))); + + std::atomic detached {false}; + std::thread detacher([&] { + pair.server->DetachPlugin(&probe); + detached = true; + }); + // The hook is still running, so DetachPlugin must not have returned. + RakSleep(200); + EXPECT_FALSE(detached.load()); + + probe.release.set_value(); + detacher.join(); + EXPECT_TRUE(detached.load()); + + const int callsAtDetach = probe.calls.load(); + for (int i = 0; i < 20; ++i) + pair.Send(); + RakSleep(300); + EXPECT_EQ(callsAtDetach, probe.calls.load()); +} + +TEST_F(NetworkThreadReceiveLive, HookNeverRunsOnceShutdownHasStarted) +{ + // Plugins learn of shutdown in OnRakPeerShutdown, which the shutting-down thread + // calls while the update thread may still be running. From then on the hook must + // not be called, or a plugin tearing down its state would race its own hook. + BlockingProbe probe; + probe.dwellMs = 2; + LivePair pair; + pair.server->AttachPlugin(&probe); + ASSERT_TRUE(pair.Start()); + + std::atomic flooding {true}; + std::thread flooder([&] { + while (flooding.load()) + { + pair.Send(); + RakSleep(1); + } + }); + const TimeMS start = GetTimeMS(); + while (GetTimeMS() - start < 5000 && probe.calls.load() < 20) + RakSleep(5); + ASSERT_GE(probe.calls.load(), 20); + + pair.server->Shutdown(0); + flooding = false; + flooder.join(); + EXPECT_TRUE(probe.shutdownStarted.load()); + EXPECT_EQ(0, probe.callsAfterShutdownStarted.load()); + pair.server->DetachPlugin(&probe); +} + +TEST_F(NetworkThreadReceiveLive, HookRunsAgainAfterARestart) +{ + BlockingProbe probe; + LivePair pair; + pair.server->AttachPlugin(&probe); + ASSERT_TRUE(pair.Start()); + pair.server->Shutdown(0); + pair.client->Shutdown(0); + probe.shutdownStarted = false; + + ASSERT_TRUE(pair.Start()); + const int before = probe.calls.load(); + pair.Send(); + const TimeMS start = GetTimeMS(); + while (GetTimeMS() - start < 5000 && probe.calls.load() == before) + RakSleep(10); + EXPECT_GT(probe.calls.load(), before); + pair.server->DetachPlugin(&probe); +} diff --git a/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp b/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp index 56360a981..836f5ab98 100644 --- a/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp +++ b/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp @@ -20,6 +20,8 @@ #include "mafianet/voice.h" #include +#include +#include #include #include @@ -122,3 +124,67 @@ TEST_F(RakVoiceNetworkThreadRelayLive, RelaysWhileTheServerNeverCallsReceive) voice.SetRelayHost(nullptr); server->DetachPlugin(&voice); } + +TEST_F(RakVoiceNetworkThreadRelayLive, ClearingTheRelayWaitsForOneThatIsMidCall) +{ + // After SetRelayHost(nullptr) returns the application may free what its relay + // captured, so the call must not return while that relay is still running. + RakVoice voice; + Peer server; + RakSleep(2); + Peer talker; + + SocketDescriptor serverSd(0, "127.0.0.1"); + ASSERT_EQ(server->Startup(1, &serverSd, 1), RAKNET_STARTED); + server->SetMaximumIncomingConnections(1); + const unsigned short serverPort = server->GetInternalID().GetPort(); + + std::promise entered, release; + std::atomic blockOnce {true}; + voice.SetRelayHost([&](Packet *) { + if (blockOnce.exchange(false)) + { + entered.set_value(); + release.get_future().wait(); + } + }); + server->AttachPlugin(&voice); + + SocketDescriptor clientSd; + ASSERT_EQ(talker->Startup(1, &clientSd, 1), RAKNET_STARTED); + ASSERT_EQ(talker->Connect("127.0.0.1", serverPort, 0, 0), CONNECTION_ATTEMPT_STARTED); + SystemAddress serverAddress; + serverAddress.SetBinaryAddress("127.0.0.1"); + serverAddress.SetPortHostOrder(serverPort); + TimeMS start = GetTimeMS(); + while (GetTimeMS() - start < 10000 && talker->GetConnectionState(serverAddress) != IS_CONNECTED) + { + for (auto pkt : talker.incoming()) (void) pkt; + RakSleep(30); + } + ASSERT_EQ(IS_CONNECTED, talker->GetConnectionState(serverAddress)); + + std::future inRelay = entered.get_future(); + unsigned short sequence = 0; + start = GetTimeMS(); + while (GetTimeMS() - start < 10000 && inRelay.wait_for(std::chrono::milliseconds(20)) != std::future_status::ready) + { + const std::vector frame = RelayFrameFrom(talker->GetMyGUID(), ++sequence); + talker->Send((const char *) frame.data(), (int) frame.size(), Priority::High, Reliability::Unreliable, 0, + serverAddress, false); + } + ASSERT_EQ(std::future_status::ready, inRelay.wait_for(std::chrono::seconds(0))); + + std::atomic cleared {false}; + std::thread clearer([&] { + voice.SetRelayHost(nullptr); + cleared = true; + }); + RakSleep(200); + EXPECT_FALSE(cleared.load()); + + release.set_value(); + clearer.join(); + EXPECT_TRUE(cleared.load()); + server->DetachPlugin(&voice); +} diff --git a/Tests/Unit/NetworkThreadReceiveTests.cpp b/Tests/Unit/NetworkThreadReceiveTests.cpp index 9733b05fa..e848ca03e 100644 --- a/Tests/Unit/NetworkThreadReceiveTests.cpp +++ b/Tests/Unit/NetworkThreadReceiveTests.cpp @@ -229,3 +229,165 @@ TEST(NetworkThreadReceive, ConnectionEventsAreNotRoutedThroughTheHook) EXPECT_EQ(1u, probe.UserThreadCount(ID_NEW_INCOMING_CONNECTION)); p.server->DetachPlugin(&probe); } + +namespace +{ + // Records the order plugins are offered a message in, and does one thing to a + // chosen id: consume it, or attach/detach a plugin from inside the hook. + class OrderProbe : public PluginInterface2 + { + public: + OrderProbe(std::vector &order, int tag) : order(order), tag(tag) {} + + PluginReceiveResult OnReceiveNetworkThread(Packet *packet) override + { + if (packet->data[0] == ID_TIMESTAMP) + sawTimestamp = true; + if (packet->data[0] < ID_USER_PACKET_ENUM && packet->data[0] != ID_TIMESTAMP) + return RR_CONTINUE_PROCESSING; + order.push_back(tag); + if (packet->data[0] == consumeId) + return RR_STOP_PROCESSING_AND_DEALLOCATE; + if (packet->data[0] == detachSelfOn) + GetRakPeerInterface()->DetachPlugin(this); + if (packet->data[0] == attachOn && toAttach != nullptr) + GetRakPeerInterface()->AttachPlugin(toAttach); + return RR_CONTINUE_PROCESSING; + } + + MessageID consumeId = 0; + MessageID detachSelfOn = 0; + MessageID attachOn = 0; + PluginInterface2 *toAttach = nullptr; + bool sawTimestamp = false; + + private: + std::vector ℴ + int tag; + }; +} + +TEST(NetworkThreadReceive, PluginsAreOfferedInAttachOrderAndTheFirstToConsumeWins) +{ + Pair p; + std::vector order; + OrderProbe first(order, 1), second(order, 2), third(order, 3); + second.consumeId = ID_NT_CONSUMED; + p.server->AttachPlugin(&first); + p.server->AttachPlugin(&second); + p.server->AttachPlugin(&third); + ASSERT_TRUE(p.Start()); + + SendId(p.client, p.net.AddressOf(p.server), ID_NT_CONSUMED); + ASSERT_TRUE(p.net.RunUntil([&] { return order.size() >= 2; }, 2000)); + p.net.Step(50); + EXPECT_EQ((std::vector {1, 2}), order); + EXPECT_EQ(0u, InboxCount(p.net, p.server, ID_NT_CONSUMED)); + p.server->DetachPlugin(&first); + p.server->DetachPlugin(&second); + p.server->DetachPlugin(&third); +} + +TEST(NetworkThreadReceive, MessageNoPluginTakesIsOfferedToEachOnceAndQueuedOnce) +{ + Pair p; + std::vector order; + OrderProbe first(order, 1), second(order, 2); + p.server->AttachPlugin(&first); + p.server->AttachPlugin(&second); + ASSERT_TRUE(p.Start()); + + SendId(p.client, p.net.AddressOf(p.server), ID_NT_PASSED); + ASSERT_TRUE(p.net.RunUntilReceived(p.server, ID_NT_PASSED, 2000)); + p.net.Step(50); + EXPECT_EQ((std::vector {1, 2}), order); + EXPECT_EQ(1u, InboxCount(p.net, p.server, ID_NT_PASSED)); + p.server->DetachPlugin(&first); + p.server->DetachPlugin(&second); +} + +TEST(NetworkThreadReceive, AttachingTheSamePluginTwiceOffersEachMessageOnce) +{ + Pair p; + std::vector order; + OrderProbe probe(order, 1); + p.server->AttachPlugin(&probe); + p.server->AttachPlugin(&probe); + ASSERT_TRUE(p.Start()); + + SendId(p.client, p.net.AddressOf(p.server), ID_NT_PASSED); + ASSERT_TRUE(p.net.RunUntilReceived(p.server, ID_NT_PASSED, 2000)); + EXPECT_EQ((std::vector {1}), order); + p.server->DetachPlugin(&probe); +} + +TEST(NetworkThreadReceive, PluginAttachedAfterTheConnectionGetsTheHook) +{ + Pair p; + ASSERT_TRUE(p.Start()); + NetworkThreadProbe probe; + p.server->AttachPlugin(&probe); + + p.net.SetReceiveEnabled(p.server, false); + SendId(p.client, p.net.AddressOf(p.server), ID_NT_CONSUMED); + EXPECT_TRUE(p.net.RunUntil([&] { return probe.NetworkThreadCount(ID_NT_CONSUMED) == 1; }, 2000)); + p.server->DetachPlugin(&probe); +} + +TEST(NetworkThreadReceive, TimestampedMessageIsOfferedUnshiftedAndStillReachesReceive) +{ + Pair p; + std::vector order; + OrderProbe probe(order, 1); + p.server->AttachPlugin(&probe); + ASSERT_TRUE(p.Start()); + + BitStream bs; + bs.Write((MessageID) ID_TIMESTAMP); + bs.Write(GetTime()); + bs.Write(ID_NT_PASSED); + ASSERT_NE(0u, p.client->Send(&bs, Priority::High, Reliability::ReliableOrdered, 0, p.net.AddressOf(p.server), false)); + ASSERT_TRUE(p.net.RunUntilReceived(p.server, ID_TIMESTAMP, 2000)); + EXPECT_TRUE(probe.sawTimestamp); + p.server->DetachPlugin(&probe); +} + +TEST(NetworkThreadReceive, DetachFromInsideTheHookIsIgnored) +{ + // Detaching there would deadlock on the dispatch lock, and would change plugin lists + // Receive() walks on the user thread. It is ignored: the plugin stays attached. + Pair p; + std::vector order; + OrderProbe probe(order, 1); + probe.detachSelfOn = ID_NT_PASSED; + p.server->AttachPlugin(&probe); + ASSERT_TRUE(p.Start()); + + SendId(p.client, p.net.AddressOf(p.server), ID_NT_PASSED); + ASSERT_TRUE(p.net.RunUntilReceived(p.server, ID_NT_PASSED, 2000)); + probe.consumeId = ID_NT_CONSUMED; + SendId(p.client, p.net.AddressOf(p.server), ID_NT_CONSUMED); + ASSERT_TRUE(p.net.RunUntil([&] { return order.size() == 2; }, 2000)); + EXPECT_EQ(p.server, probe.GetRakPeerInterface()); + p.server->DetachPlugin(&probe); + EXPECT_EQ(nullptr, probe.GetRakPeerInterface()); +} + +TEST(NetworkThreadReceive, AttachFromInsideTheHookIsIgnored) +{ + Pair p; + std::vector order; + OrderProbe attacher(order, 1), attached(order, 2); + attacher.attachOn = ID_NT_PASSED; + attacher.toAttach = &attached; + p.server->AttachPlugin(&attacher); + ASSERT_TRUE(p.Start()); + + SendId(p.client, p.net.AddressOf(p.server), ID_NT_PASSED); + ASSERT_TRUE(p.net.RunUntilReceived(p.server, ID_NT_PASSED, 2000)); + SendId(p.client, p.net.AddressOf(p.server), ID_NT_CONSUMED); + ASSERT_TRUE(p.net.RunUntilReceived(p.server, ID_NT_CONSUMED, 2000)); + EXPECT_EQ((std::vector {1, 1}), order); + EXPECT_EQ(nullptr, attached.GetRakPeerInterface()); + p.server->DetachPlugin(&attacher); +} diff --git a/Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp b/Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp index 5ddff8bec..6bc4f39ad 100644 --- a/Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp +++ b/Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp @@ -115,3 +115,209 @@ TEST(RakVoiceNetworkThreadRelay, ClearingTheRelayHostStopsRelaying) EXPECT_EQ(0, relayed.load()); EXPECT_FALSE(rig.net.Received(rig.listener, ID_RAKVOICE_RELAY_DATA)); } + +// --- Direct calls: the hooks without a network ------------------------------ + +namespace +{ + Packet View(std::vector &bytes, RakNetGUID from) + { + Packet p {}; + p.data = bytes.data(); + p.length = (unsigned int) bytes.size(); + p.guid = from; + return p; + } + + RakNetGUID Guid(uint64_t g) + { + RakNetGUID guid; + guid.g = g; + return guid; + } +} + +TEST(RakVoiceNetworkThreadRelay, OnlyRelayFramesAreTakenOnTheNetworkThread) +{ + RakVoice voice; + int relayed = 0; + voice.SetRelayHost([&](Packet *) { ++relayed; }); + + for (unsigned char id : {(unsigned char) ID_RAKVOICE_DATA, (unsigned char) ID_RAKVOICE_OPEN_CHANNEL_REQUEST, + (unsigned char) ID_RAKVOICE_CLOSE_CHANNEL, (unsigned char) ID_USER_PACKET_ENUM}) + { + std::vector bytes {id, 1, 2, 3}; + Packet p = View(bytes, Guid(7)); + EXPECT_EQ(RR_CONTINUE_PROCESSING, voice.OnReceiveNetworkThread(&p)) << "id=" << int(id); + } + EXPECT_EQ(0, relayed); + + std::vector frame = RelayFrameFrom(Guid(7)); + Packet p = View(frame, Guid(7)); + EXPECT_EQ(RR_STOP_PROCESSING_AND_DEALLOCATE, voice.OnReceiveNetworkThread(&p)); + EXPECT_EQ(1, relayed); +} + +TEST(RakVoiceNetworkThreadRelay, EmptyOrNullPacketIsLeftAlone) +{ + RakVoice voice; + int relayed = 0; + voice.SetRelayHost([&](Packet *) { ++relayed; }); + + Packet empty {}; + EXPECT_EQ(RR_CONTINUE_PROCESSING, voice.OnReceiveNetworkThread(&empty)); + EXPECT_EQ(RR_CONTINUE_PROCESSING, voice.OnReceiveNetworkThread(nullptr)); + EXPECT_EQ(0, relayed); +} + +TEST(RakVoiceNetworkThreadRelay, NotARelayHostLeavesRelayFramesForReceive) +{ + RakVoice voice; + std::vector frame = RelayFrameFrom(Guid(7)); + Packet p = View(frame, Guid(7)); + EXPECT_EQ(RR_CONTINUE_PROCESSING, voice.OnReceiveNetworkThread(&p)); +} + +TEST(RakVoiceNetworkThreadRelay, TheRelayIsHandedThePacketItWasGiven) +{ + RakVoice voice; + Packet *seen = nullptr; + voice.SetRelayHost([&](Packet *packet) { seen = packet; }); + + std::vector frame = RelayFrameFrom(Guid(7)); + Packet p = View(frame, Guid(7)); + voice.OnReceiveNetworkThread(&p); + EXPECT_EQ(&p, seen); +} + +TEST(RakVoiceNetworkThreadRelay, ReplacingTheRelayRoutesLaterFramesToTheNewOne) +{ + RakVoice voice; + int first = 0, second = 0; + voice.SetRelayHost([&](Packet *) { ++first; }); + voice.SetRelayHost([&](Packet *) { ++second; }); + + std::vector frame = RelayFrameFrom(Guid(7)); + Packet p = View(frame, Guid(7)); + voice.OnReceiveNetworkThread(&p); + EXPECT_EQ(0, first); + EXPECT_EQ(1, second); +} + +TEST(RakVoiceNetworkThreadRelay, RelayCanClearItselfFromInside) +{ + // Must not deadlock, and must not destroy the running relay under itself. + RakVoice voice; + int relayed = 0; + std::vector captured(64, 1); // gives the lambda state worth corrupting + voice.SetRelayHost([&voice, &relayed, captured](Packet *) { + voice.SetRelayHost(nullptr); + relayed += captured[63]; + }); + + std::vector frame = RelayFrameFrom(Guid(7)); + Packet p = View(frame, Guid(7)); + EXPECT_EQ(RR_STOP_PROCESSING_AND_DEALLOCATE, voice.OnReceiveNetworkThread(&p)); + EXPECT_EQ(1, relayed); + EXPECT_EQ(RR_CONTINUE_PROCESSING, voice.OnReceiveNetworkThread(&p)); + EXPECT_EQ(1, relayed); +} + +TEST(RakVoiceNetworkThreadRelay, RelayCanReplaceItselfFromInside) +{ + RakVoice voice; + int first = 0, second = 0; + voice.SetRelayHost([&](Packet *) { + ++first; + voice.SetRelayHost([&](Packet *) { ++second; }); + }); + + std::vector frame = RelayFrameFrom(Guid(7)); + Packet p = View(frame, Guid(7)); + voice.OnReceiveNetworkThread(&p); + voice.OnReceiveNetworkThread(&p); + EXPECT_EQ(1, first); + EXPECT_EQ(1, second); +} + +TEST(RakVoiceNetworkThreadRelay, RelayHostDropsAStragglerFrameThatReachedReceive) +{ + // A frame queued just before SetRelayHost took effect still reaches OnReceive; + // a relay host never decodes, so it is dropped rather than decoded or passed on. + RakVoice voice; + voice.SetRelayHost([](Packet *) {}); + std::vector frame = RelayFrameFrom(Guid(7)); + Packet p = View(frame, Guid(7)); + EXPECT_EQ(RR_STOP_PROCESSING_AND_DEALLOCATE, voice.OnReceive(&p)); +} + +// --- On the simulated network ----------------------------------------------- + +TEST(RakVoiceNetworkThreadRelay, SpoofedOriginIsNotRelayed) +{ + // The anti-impersonation check in RelayFrame must hold on the network thread too. + RelayRig rig; + ASSERT_TRUE(rig.Start()); + const RakNetGUID listener = rig.listener->GetMyGUID(); + std::atomic relayed {0}; + rig.voice.SetRelayHost([&](Packet *packet) { + ++relayed; + rig.voice.RelayFrame(packet, &listener, 1); + }); + + const std::vector spoofed = RelayFrameFrom(listener); // talker claims to be the listener + rig.talker->Send((const char *) spoofed.data(), (int) spoofed.size(), Priority::High, Reliability::Unreliable, 0, + rig.net.AddressOf(rig.server), false); + ASSERT_TRUE(rig.net.RunUntil([&] { return relayed.load() == 1; }, 2000)); + rig.net.Step(200); + EXPECT_FALSE(rig.net.Received(rig.listener, ID_RAKVOICE_RELAY_DATA)); +} + +TEST(RakVoiceNetworkThreadRelay, FrameQueuedBeforeBecomingRelayHostIsDropped) +{ + RelayRig rig; + ASSERT_TRUE(rig.Start()); + rig.net.SetReceiveEnabled(rig.server, false); + rig.Talk(); + rig.net.Step(200); // the frame is now queued for the server's Receive() + + std::atomic relayed {0}; + rig.voice.SetRelayHost([&](Packet *) { ++relayed; }); + rig.net.SetReceiveEnabled(rig.server, true); + rig.net.Step(50); + EXPECT_EQ(0, relayed.load()); + EXPECT_FALSE(rig.net.Received(rig.server, ID_RAKVOICE_RELAY_DATA)); +} + +TEST(RakVoiceNetworkThreadRelay, EveryFrameOfABurstIsRelayedInOrder) +{ + RelayRig rig; + ASSERT_TRUE(rig.Start()); + const RakNetGUID listener = rig.listener->GetMyGUID(); + rig.voice.SetRelayHost([&](Packet *packet) { rig.voice.RelayFrame(packet, &listener, 1); }); + rig.net.SetReceiveEnabled(rig.server, false); + + const unsigned short kFrames = 50; + for (unsigned short seq = 1; seq <= kFrames; ++seq) + { + std::vector frame = RelayFrameFrom(rig.talker->GetMyGUID()); + std::memcpy(frame.data() + RAKVOICE_RELAY_OFFSET_SEQUENCE, &seq, sizeof(seq)); + rig.talker->Send((const char *) frame.data(), (int) frame.size(), Priority::High, Reliability::Unreliable, 0, + rig.net.AddressOf(rig.server), false); + rig.net.Step(20); + } + rig.net.Step(200); + + std::vector sequences; + for (const auto &m : rig.net.Inbox(rig.listener)) + { + if (m.id != ID_RAKVOICE_RELAY_DATA) + continue; + unsigned short seq = 0; + std::memcpy(&seq, m.data.data() + RAKVOICE_RELAY_OFFSET_SEQUENCE, sizeof(seq)); + sequences.push_back(seq); + } + ASSERT_EQ(kFrames, sequences.size()); + for (unsigned short i = 0; i < kFrames; ++i) + EXPECT_EQ(i + 1, sequences[i]); +} From 22e86a6cdefe9df8e30be2d5a091ed6fb9e0471a Mon Sep 17 00:00:00 2001 From: Segfault <5221072+Segfaultd@users.noreply.github.com> Date: Sun, 18 Oct 2026 11:48:45 +0200 Subject: [PATCH 4/8] chore: restore peer.cpp CRLF line endings The previous commit rewrote every line of peer.cpp from CRLF to LF. This puts the original line endings back; no content changes. --- Source/src/peer.cpp | 14470 +++++++++++++++++++++--------------------- 1 file changed, 7235 insertions(+), 7235 deletions(-) diff --git a/Source/src/peer.cpp b/Source/src/peer.cpp index 41ffe1b80..3d6ca5aea 100644 --- a/Source/src/peer.cpp +++ b/Source/src/peer.cpp @@ -1,7235 +1,7235 @@ -/* - * Original work: Copyright (c) 2014, Oculus VR, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * RakNet License.txt file in the licenses directory of this source tree. An additional grant - * of patent rights can be found in the RakNet Patents.txt file in the same directory. - * - * - * Modified work: Copyright (c) 2016-2020, SLikeSoft UG (haftungsbeschränkt) - * - * This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style - * license found in the license.txt file in the root directory of this source tree. - */ - -// \file -// - - - -#define CAT_NEUTER_EXPORT /* Neuter dllimport for libcat */ - -#include "mafianet/defines.h" -#include "mafianet/peer.h" -#include "mafianet/session_admission.h" -#include "mafianet/types.h" - -#ifdef _WIN32 - -#else -#include -#endif - -// #if defined(new) -// #pragma push_macro("new") -// #undef new -// #define RMO_NEW_UNDEF_ALLOCATING_QUEUE -// #endif - -#include -#include // toupper -#include -#include "mafianet/get_time.h" -#include -#include -#include "mafianet/message_identifiers.h" -#include "mafianet/mtu_black_hole.h" -#include "mafianet/ds_huffman_encoding_tree.h" -#include "mafianet/rand.h" -#include "mafianet/plugin_interface2.h" -#include "mafianet/string_compressor.h" -#include "mafianet/string_table.h" -#include "mafianet/network_id_object.h" -#include "mafianet/types.h" -#include "mafianet/dr_sha1.h" -#include "mafianet/sleep.h" -#include "mafianet/assert.h" -#include "mafianet/version.h" -#include "mafianet/network_id_manager.h" -#include "mafianet/get_time_of_day.h" -#include "mafianet/signaled_event.h" -#include "mafianet/super_fast_hash.h" -#include "mafianet/alloca.h" -#include "mafianet/wsa_startup_singleton.h" -#include "mafianet/linux_adapter.h" -#include "mafianet/osx_adapter.h" - -#ifdef USE_THREADED_SEND -#include "mafianet/send_to_thread.h" -#endif - -#ifdef CAT_AUDIT -#define CAT_AUDIT_PRINTF(...) printf(__VA_ARGS__) -#else -#define CAT_AUDIT_PRINTF(...) -#endif - -namespace MafiaNet -{ -RAK_THREAD_DECLARATION(UpdateNetworkLoop); -RAK_THREAD_DECLARATION(RecvFromLoop); -RAK_THREAD_DECLARATION(UDTConnect); -} -#define REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE 8 - -#if !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ ) -#include // malloc -#endif - - - -#if defined(_WIN32) -// -#else -/* -#include // Console 2 -#include -extern bool _extern_Console2LoadModules(void); -extern int _extern_Console2GetConnectionStatus(void); -extern int _extern_Console2GetLobbyStatus(void); -//extern bool Console2StartupFluff(unsigned int *); -extern void Console2ShutdownFluff(void); -//extern unsigned int Console2ActivateConnection(unsigned int, void *); -//extern bool Console2BlockOnEstablished(void); -extern void Console2GetIPAndPort(unsigned int, char *, unsigned short *, unsigned int ); -//extern void Console2DeactivateConnection(unsigned int, unsigned int); -*/ -#endif - - -// Probed high to low while connecting: the connecting peer pads -// ID_OPEN_CONNECTION_REQUEST_1 to a rung and steps down when nothing comes back, -// so the negotiated MTU is the largest rung that survived the path. Each rung is -// a whole IP datagram, MAXIMUM_MTU_SIZE first. -// -// 1280 is both the IPv6 minimum MTU and what WireGuard-derived tunnels commonly -// settle on, which makes it the single most valuable intermediate rung; 1024 -// covers heavier or stacked encapsulation; 576 is the dial-up floor. The gap -// from MAXIMUM_MTU_SIZE straight to 1200 that used to sit here meant a peer one -// byte over the top rung gave up ~20% of its payload capacity to find that out. -// -// The ladder itself lives in mtu_black_hole.h: the in-session black-hole -// step-down (ReliabilityLayer) walks the same rungs this handshake probes. -static const int NUM_MTU_SIZES=MafiaNet::MTU_LADDER_SIZE; -static const int *const mtuSizes=MafiaNet::MTU_LADDER; - -// How many connection attempts are spent on each rung of mtuSizes before -// stepping down. -// -// Never zero. Connect() takes sendConnectionAttemptCount from the caller and -// documents no lower bound, so a caller asking for fewer attempts than there are -// rungs used to divide by zero here -- a crash that was unreachable only because -// the default (12) happened to exceed NUM_MTU_SIZES. Such a caller now gets one -// attempt per rung and stops when its budget runs out, having tried the largest -// sizes first. -static unsigned GetConnectionAttemptsPerMTU(unsigned sendConnectionAttemptCount) -{ - const unsigned attemptsPerMTU = sendConnectionAttemptCount / (unsigned) NUM_MTU_SIZES; - return attemptsPerMTU > 0 ? attemptsPerMTU : 1; -} - - -// Note to self - if I change this it might affect RECIPIENT_OFFLINE_MESSAGE_INTERVAL in Natpunchthrough.cpp -//static const int MAX_OPEN_CONNECTION_REQUESTS=8; -//static const int TIME_BETWEEN_OPEN_CONNECTION_REQUESTS=500; - -using namespace MafiaNet; - -static RakNetRandom rnr; - -/* -struct RakPeerAndIndex -{ - RakNetSocket2 *s; - RakPeer *rakPeer; -}; -*/ - -static const unsigned int MAX_OFFLINE_DATA_LENGTH=400; // I set this because I limit ID_CONNECTION_REQUEST to 512 bytes, and the password is appended to that packet. - -// Used to distinguish between offline messages with data, and messages from the reliability layer -// Should be different than any message that could result from messages from the reliability layer -// Make sure highest bit is 0, so isValid in DatagramHeaderFormat is false -static const unsigned char OFFLINE_MESSAGE_DATA_ID[16]={0x00,0xFF,0xFF,0x00,0xFE,0xFE,0xFE,0xFE,0xFD,0xFD,0xFD,0xFD,0x12,0x34,0x56,0x78}; - -struct PacketFollowedByData -{ - Packet p; - unsigned char data[1]; -}; - -Packet *RakPeer::AllocPacket(unsigned dataSize, const char *file, unsigned int line) -{ - // Crashes when dataSize is 4 bytes - not sure why -// unsigned char *data = (unsigned char *) rakMalloc_Ex(sizeof(PacketFollowedByData)+dataSize, file, line); -// Packet *p = &((PacketFollowedByData *)data)->p; -// p->data=((PacketFollowedByData *)data)->data; -// p->length=dataSize; -// p->bitSize=BYTES_TO_BITS(dataSize); -// p->deleteData=false; -// p->guid=UNASSIGNED_RAKNET_GUID; -// return p; - - MafiaNet::Packet *p; - packetAllocationPoolMutex.Lock(); - p = packetAllocationPool.Allocate(file,line); - ++packetsOutstanding; - packetAllocationPoolMutex.Unlock(); - p = new ((void*)p) Packet; - p->data=(unsigned char*) rakMalloc_Ex(dataSize,file,line); - p->length=dataSize; - p->bitSize=BYTES_TO_BITS(dataSize); - p->deleteData=true; - p->guid=UNASSIGNED_RAKNET_GUID; - p->wasGeneratedLocally=false; - return p; -} - -Packet *RakPeer::AllocPacket(unsigned dataSize, unsigned char *data, const char *file, unsigned int line) -{ - // Packet *p = (Packet *)rakMalloc_Ex(sizeof(Packet), file, line); - MafiaNet::Packet *p; - packetAllocationPoolMutex.Lock(); - p = packetAllocationPool.Allocate(file,line); - ++packetsOutstanding; - packetAllocationPoolMutex.Unlock(); - p = new ((void*)p) Packet; - RakAssert(p); - p->data=data; - p->length=dataSize; - p->bitSize=BYTES_TO_BITS(dataSize); - p->deleteData=true; - p->guid=UNASSIGNED_RAKNET_GUID; - p->wasGeneratedLocally=false; - return p; -} - -STATIC_FACTORY_DEFINITIONS(RakPeerInterface,RakPeer) - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Constructor -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RakPeer() -{ -#if LIBCAT_SECURITY==1 - // Encryption and security - CAT_AUDIT_PRINTF("AUDIT: Initializing RakPeer security flags: using_security = false, server_handshake = null, cookie_jar = null\n"); - _using_security = false; - _server_handshake = 0; - _cookie_jar = 0; -#endif - - StringCompressor::AddReference(); - MafiaNet::StringTable::AddReference(); - WSAStartupSingleton::AddRef(); - - defaultMTUSize = mtuSizes[NUM_MTU_SIZES-1]; - trackFrequencyTable = false; - maximumIncomingConnections = 0; - maximumNumberOfPeers = 0; - //remoteSystemListSize=0; - remoteSystemList = 0; - activeSystemList = 0; - activeSystemListSize=0; - remoteSystemLookup=0; - bytesSentPerSecond = bytesReceivedPerSecond = 0; - endThreads = true; - networkThreadReceiveStopped = false; - isMainLoopThreadActive = false; - socketFactory = nullptr; - manualUpdate = false; - incomingDatagramEventHandler=0; - sessionConfigInteractive=false; - maximumPendingSessions=0; - maximumPendingSessionsPerAddress=0; - sessionTimeout=0; - - - - - - // isRecvfromThreadActive=false; -#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 - occasionalPing = true; -#else - occasionalPing = false; -#endif - allowInternalRouting=false; - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - ipList[i]=UNASSIGNED_SYSTEM_ADDRESS; - allowConnectionResponseIPMigration = false; - //incomingPasswordLength=outgoingPasswordLength=0; - incomingPasswordLength=0; - splitMessageProgressInterval=0; - //unreliableTimeout=0; - unreliableTimeout=1000; - maxOutgoingBPS=0; - firstExternalID=UNASSIGNED_SYSTEM_ADDRESS; - myGuid=UNASSIGNED_RAKNET_GUID; - userUpdateThreadPtr=0; - userUpdateThreadData=0; - -#ifdef _DEBUG - // Wait longer to disconnect in debug so I don't get disconnected while tracing - defaultTimeoutTime=30000; -#else - defaultTimeoutTime=10000; -#endif - -#ifdef _DEBUG - _packetloss=0.0; - _minExtraPing=0; - _extraPingVariance=0; -#endif - - bufferedCommands.SetPageSize(sizeof(BufferedCommandStruct)*16); - socketQueryOutput.SetPageSize(sizeof(SocketQueryOutput)*8); - - packetAllocationPoolMutex.Lock(); - packetAllocationPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); - packetsOutstanding = 0; - packetAllocationPoolMutex.Unlock(); - shutdownGeneration = 0; - - remoteSystemIndexPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - GenerateGUID(); - - quitAndDataEvents.InitEvent(); - limitConnectionFrequencyFromTheSameIP=false; - ResetSendReceipt(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Destructor -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::~RakPeer() -{ - Shutdown( 0, 0 ); - - // Free the ban list. - ClearBanList(); - - StringCompressor::RemoveReference(); - MafiaNet::StringTable::RemoveReference(); - WSAStartupSingleton::Deref(); - - quitAndDataEvents.CloseEvent(); - -#if LIBCAT_SECURITY==1 - // Encryption and security - CAT_AUDIT_PRINTF("AUDIT: Deleting RakPeer security objects, handshake = %x, cookie jar = %x\n", _server_handshake, _cookie_jar); - if (_server_handshake) MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - if (_cookie_jar) MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); -#endif - - - - - - - - - - - - - - - - -// for (unsigned int i=0; i < pluginListTS.Size(); i++) -// pluginListTS[i]->SetRakPeerInterface(0); -// for (unsigned int i=0; i < pluginListNTS.Size(); i++) -// pluginListNTS[i]->SetRakPeerInterface(0); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// \brief Starts the network threads, opens the listen port. -// You must call this before calling Connect(). -// Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). -// \note Call SetMaximumIncomingConnections if you want to accept incoming connections -// \param[in] maxConnections The maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.- A hybrid would set it to the sum of both types of connections -// \param[in] localPort The port to listen for connections on. -// \param[in] _threadSleepTimer How many ms to Sleep each internal update cycle. With new congestion control, the best results will be obtained by passing 10. -// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass &socketDescriptor, 1SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); -// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. -// \return False on failure (can't create socket or thread), true on success. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -StartupResult RakPeer::Startup( unsigned int maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority ) -{ - if (IsActive()) - return RAKNET_ALREADY_STARTED; - - SetNetworkThreadReceiveStopped(false); - - // If getting the guid failed in the constructor, try again - if (myGuid.g==0) - { - GenerateGUID(); - if (myGuid.g==0) - return COULD_NOT_GENERATE_GUID; - } - - if (threadPriority==-99999) - { - - -#if defined(_WIN32) - threadPriority=0; - - - - -#else - threadPriority=1000; -#endif - } - - - FillIPList(); - - if (myGuid==UNASSIGNED_RAKNET_GUID) - { - rnr.SeedMT( GenerateSeedFromGuid() ); - } - - //RakPeerAndIndex rpai[32]; - //RakAssert(socketDescriptorCount<32); - - RakAssert(socketDescriptors && socketDescriptorCount>=1); - - if (socketDescriptors==0 || socketDescriptorCount<1) - return INVALID_SOCKET_DESCRIPTORS; - - //unsigned short localPort; - //localPort=socketDescriptors[0].port; - - RakAssert( maxConnections > 0 ); - - if ( maxConnections <= 0 ) - return INVALID_MAX_CONNECTIONS; - - DerefAllSockets(); - - - unsigned i; - // Go through all socket descriptors and precreate sockets on the specified addresses - for (i=0; iCreate(socketDescriptors[i], i) : RakNetSocket2Allocator::AllocRNS2(); - if (r2 == nullptr) - { - DerefAllSockets(); - return SOCKET_PORT_ALREADY_IN_USE; - } - r2->SetUserConnectionSocketIndex(i); - if (r2->IsBerkleySocket()) - { - RNS2_BerkleyBindParameters bbp; - bbp.port=socketDescriptors[i].port; - bbp.hostAddress=(char*) socketDescriptors[i].hostAddress; - bbp.addressFamily=socketDescriptors[i].socketFamily; - bbp.type=SOCK_DGRAM; - bbp.protocol=socketDescriptors[i].extraSocketOptions; - bbp.nonBlockingSocket=false; - bbp.setBroadcast=true; - bbp.setIPHdrIncl=false; - bbp.doNotFragment=false; - bbp.pollingThreadPriority=threadPriority; - bbp.eventHandler=this; - RNS2BindResult br = ((RNS2_Berkley*) r2)->Bind(&bbp, _FILE_AND_LINE_); - - if ( - #if RAKNET_SUPPORT_IPV6==0 - socketDescriptors[i].socketFamily!=AF_INET || - #endif - br==BR_REQUIRES_RAKNET_SUPPORT_IPV6_DEFINE) - { - RakNetSocket2Allocator::DeallocRNS2(r2); - DerefAllSockets(); - return SOCKET_FAMILY_NOT_SUPPORTED; - } - else if (br==BR_FAILED_TO_BIND_SOCKET) - { - RakNetSocket2Allocator::DeallocRNS2(r2); - DerefAllSockets(); - return SOCKET_PORT_ALREADY_IN_USE; - } - else if (br==BR_FAILED_SEND_TEST) - { - RakNetSocket2Allocator::DeallocRNS2(r2); - DerefAllSockets(); - return SOCKET_FAILED_TEST_SEND; - } - else - { - RakAssert(br==BR_SUCCESS); - } - } - else - { - // Factory-supplied: already bound; it delivers datagrams to us. - r2->SetRecvEventHandler(this); - } - - socketList.Push(r2, _FILE_AND_LINE_ ); - - } - - for (i=0; iIsBerkleySocket()) - ((RNS2_Berkley*) socketList[i])->CreateRecvPollingThread(threadPriority); - } - - for (i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - { - if (ipList[i]==UNASSIGNED_SYSTEM_ADDRESS) - break; - // #high - using the 1st socket here is flawed - in cases of having multiple sockets (f.e. different ports and different families (i.e. IPv4/IPv6) we must use the proper - // socket for each IP address in the list - if (socketList[0]->IsBerkleySocket()) - { - unsigned short port = ((RNS2_Berkley*)socketList[0])->GetBoundAddress().GetPort(); - ipList[i].SetPortHostOrder(port); - - } - else - { - ipList[i].SetPortHostOrder(socketList[0]->GetBoundAddress().GetPort()); - } - } - - if ( maximumNumberOfPeers == 0 ) - { - // Don't allow more incoming connections than we have peers. - if ( maximumIncomingConnections > maxConnections ) - maximumIncomingConnections = maxConnections; - - maximumNumberOfPeers = maxConnections; - // 04/19/2006 - Don't overallocate because I'm no longer allowing connected pings. - // The disconnects are not consistently processed and the process was sloppy and complicated. - // Allocate 10% extra to handle new connections from players trying to connect when the server is full - //remoteSystemListSize = maxConnections;// * 11 / 10 + 1; - - // remoteSystemList in Single thread - //remoteSystemList = MafiaNet::OP_NEW( _FILE_AND_LINE_ ); - remoteSystemList = MafiaNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); - - remoteSystemLookup = MafiaNet::OP_NEW_ARRAY((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE, _FILE_AND_LINE_ ); - - activeSystemList = MafiaNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); - - for ( i = 0; i < maximumNumberOfPeers; i++ ) - //for ( i = 0; i < remoteSystemListSize; i++ ) - { - // remoteSystemList in Single thread - remoteSystemList[ i ].isActive = false; - remoteSystemList[ i ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; - remoteSystemList[ i ].guid = UNASSIGNED_RAKNET_GUID; - remoteSystemList[ i ].myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; - remoteSystemList[ i ].connectMode=RemoteSystemStruct::NO_ACTION; - remoteSystemList[ i ].MTUSize = defaultMTUSize; - remoteSystemList[ i ].remoteSystemIndex = (SystemIndex) i; - // One-time zero-init: the array is OP_NEW_ARRAY allocated with no member init, so prime the - // reason-payload fields here before ClearDisconnectReason() can ever free them. - remoteSystemList[ i ].disconnectReasonData = 0; - remoteSystemList[ i ].disconnectReasonLength = 0; - remoteSystemList[ i ].remoteSessionConfigData = 0; - remoteSystemList[ i ].remoteSessionConfigLength = 0; - remoteSystemList[ i ].withheldConnectionPacketData = 0; - remoteSystemList[ i ].withheldConnectionPacketLength = 0; - remoteSystemList[ i ].sessionConfigAwaitingLocalDecision = false; - remoteSystemList[ i ].sessionConfigIsConnectingSide = false; - remoteSystemList[ i ].connectionReportedToApplication = false; - remoteSystemList[ i ].sessionConfigStartTime = 0; -#ifdef _DEBUG - remoteSystemList[ i ].reliabilityLayer.ApplyNetworkSimulator(_packetloss, _minExtraPing, _extraPingVariance); -#endif - - // All entries in activeSystemList have valid pointers all the time. - activeSystemList[ i ] = &remoteSystemList[ i ]; - } - - for (i=0; i < (unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE; i++) - { - remoteSystemLookup[i]=0; - } - } - - // For histogram statistics - // nextReadBytesTime=0; - // lastSentBytes=lastReceivedBytes=0; - - if ( endThreads ) - { - updateCycleIsRunning = false; - endThreads = false; - firstExternalID=UNASSIGNED_SYSTEM_ADDRESS; - - ClearBufferedCommands(); - ClearBufferedPackets(); - ClearSocketQueryOutput(); - - if ( isMainLoopThreadActive == false && !manualUpdate ) - { -#if RAKPEER_USER_THREADED!=1 - - int errorCode; - - - - - - - - errorCode = MafiaNet::RakThread::Create(UpdateNetworkLoop, this, threadPriority); - - - if ( errorCode != 0 ) - { - Shutdown( 0, 0 ); - return FAILED_TO_CREATE_NETWORK_THREAD; - } -// RakAssert(isRecvFromLoopThreadActive.GetValue()==0); -#endif // RAKPEER_USER_THREADED!=1 - - /* - for (i=0; i(_FILE_AND_LINE_); - rpai->s=socketList[i]; - rpai->rakPeer=this; - -#if RAKPEER_USER_THREADED!=1 - - errorCode = MafiaNet::RakThread::Create(RecvFromLoop, rpai, threadPriority); - - if ( errorCode != 0 ) - { - Shutdown( 0, 0 ); - return FAILED_TO_CREATE_NETWORK_THREAD; - } -#endif // RAKPEER_USER_THREADED!=1 - } - */ - - - /* -#if RAKPEER_USER_THREADED!=1 - - while ( isRecvFromLoopThreadActive.GetValue() < (uint32_t) socketDescriptorCount ) - RakSleep(10); - #endif // RAKPEER_USER_THREADED!=1 - */ - - } - -#if RAKPEER_USER_THREADED!=1 - // Wait for the threads to activate. When they are active they will set these variables to true - while ( !manualUpdate && isMainLoopThreadActive == false ) - RakSleep(10); -#endif // RAKPEER_USER_THREADED!=1 - } - - for (i=0; i < pluginListTS.Size(); i++) - { - pluginListTS[i]->OnRakPeerStartup(); - } - - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginListNTS[i]->OnRakPeerStartup(); - } - -#ifdef USE_THREADED_SEND - MafiaNet::SendToThread::AddRef(); -#endif - - return RAKNET_STARTED; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Must be called while offline -// -// If you accept connections, you must call this or else security will not be enabled for incoming connections. -// -// This feature requires more round trips, bandwidth, and CPU time for the connection handshake -// x64 builds require under 25% of the CPU time of other builds -// -// See the Encryption sample for example usage -// -// Parameters: -// publicKey = A pointer to the public key for accepting new connections -// privateKey = A pointer to the private key for accepting new connections -// If the private keys are 0, then a new key will be generated when this function is called -// bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::InitializeSecurity(const char *public_key, const char *private_key, bool bRequireClientKey) -{ -#if LIBCAT_SECURITY==1 - if ( endThreads == false ) - return false; - - // Copy client public key requirement flag - _require_client_public_key = bRequireClientKey; - - if (_server_handshake) - { - CAT_AUDIT_PRINTF("AUDIT: Deleting old server_handshake %x\n", _server_handshake); - MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - } - if (_cookie_jar) - { - CAT_AUDIT_PRINTF("AUDIT: Deleting old cookie jar %x\n", _cookie_jar); - MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); - } - - _server_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); - _cookie_jar = MafiaNet::OP_NEW(_FILE_AND_LINE_); - - CAT_AUDIT_PRINTF("AUDIT: Created new server_handshake %x\n", _server_handshake); - CAT_AUDIT_PRINTF("AUDIT: Created new cookie jar %x\n", _cookie_jar); - CAT_AUDIT_PRINTF("AUDIT: Running _server_handshake->Initialize()\n"); - - if (_server_handshake->Initialize(public_key, private_key)) - { - CAT_AUDIT_PRINTF("AUDIT: Successfully initialized, filling cookie jar with goodies, storing public key and setting using security flag to true\n"); - - _server_handshake->FillCookieJar(_cookie_jar); - - memcpy(my_public_key, public_key, sizeof(my_public_key)); - - _using_security = true; - return true; - } - - CAT_AUDIT_PRINTF("AUDIT: Failure to initialize so deleting server handshake and cookie jar; also setting using_security flag = false\n"); - - MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - _server_handshake=0; - MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); - _cookie_jar=0; - _using_security = false; - return false; -#else - (void) public_key; - (void) private_key; - (void) bRequireClientKey; - - return false; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description -// Must be called while offline -// Disables security for incoming connections. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DisableSecurity( void ) -{ -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: DisableSecurity() called, so deleting _server_handshake %x and cookie_jar %x\n", _server_handshake, _cookie_jar); - MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); - _server_handshake=0; - MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); - _cookie_jar=0; - - _using_security = false; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AddToSecurityExceptionList(const char *ip) -{ - securityExceptionMutex.Lock(); - securityExceptionList.Insert(RakString(ip), _FILE_AND_LINE_); - securityExceptionMutex.Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RemoveFromSecurityExceptionList(const char *ip) -{ - if (securityExceptionList.Size()==0) - return; - - if (ip==0) - { - securityExceptionMutex.Lock(); - securityExceptionList.Clear(false, _FILE_AND_LINE_); - securityExceptionMutex.Unlock(); - } - else - { - unsigned i=0; - securityExceptionMutex.Lock(); - while (i < securityExceptionList.Size()) - { - if (securityExceptionList[i].IPAddressMatch(ip)) - { - securityExceptionList[i]=securityExceptionList[securityExceptionList.Size()-1]; - securityExceptionList.RemoveAtIndex(securityExceptionList.Size()-1); - } - else - i++; - } - securityExceptionMutex.Unlock(); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsInSecurityExceptionList(const char *ip) -{ - if (securityExceptionList.Size()==0) - return false; - - unsigned i=0; - securityExceptionMutex.Lock(); - for (; i < securityExceptionList.Size(); i++) - { - if (securityExceptionList[i].IPAddressMatch(ip)) - { - securityExceptionMutex.Unlock(); - return true; - } - } - securityExceptionMutex.Unlock(); - return false; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sets how many incoming connections are allowed. If this is less than the number of players currently connected, no -// more players will be allowed to connect. If this is greater than the maximum number of peers allowed, it will be reduced -// to the maximum number of peers allowed. Defaults to 0. -// -// Parameters: -// numberAllowed - Maximum number of incoming connections allowed. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetMaximumIncomingConnections( unsigned short numberAllowed ) -{ - maximumIncomingConnections = numberAllowed; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the maximum number of incoming connections, which is always <= maxConnections -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetMaximumIncomingConnections( void ) const -{ - return maximumIncomingConnections; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns how many open connections there are at this time -// \return the number of open connections -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned short RakPeer::NumberOfConnections(void) const -{ - DataStructures::List addresses; - DataStructures::List guids; - GetSystemList(addresses, guids); - return (unsigned short) addresses.Size(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sets the password incoming connections must match in the call to Connect (defaults to none) -// Pass 0 to passwordData to specify no password -// -// Parameters: -// passwordData: A data block that incoming connections must match. This can be just a password, or can be a stream of data. -// - Specify 0 for no password data -// passwordDataLength: The length in bytes of passwordData -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetIncomingPassword( const char* passwordData, int passwordDataLength ) -{ - //if (passwordDataLength > MAX_OFFLINE_DATA_LENGTH) - // passwordDataLength=MAX_OFFLINE_DATA_LENGTH; - - if (passwordDataLength > 255) - passwordDataLength=255; - - if (passwordData==0) - passwordDataLength=0; - - // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? - // It won't overflow at least because incomingPasswordLength is an unsigned char - if (passwordDataLength>0) - memcpy(incomingPassword, passwordData, passwordDataLength); - incomingPasswordLength=(unsigned char)passwordDataLength; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetIncomingPassword( char* passwordData, int *passwordDataLength ) -{ - if (passwordData==0) - { - *passwordDataLength=incomingPasswordLength; - return; - } - - if (*passwordDataLength > incomingPasswordLength) - *passwordDataLength=incomingPasswordLength; - - if (*passwordDataLength>0) - memcpy(passwordData, incomingPassword, *passwordDataLength); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Call this to connect to the specified host (ip or domain name) and server port. -// Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. Calling both acts as a true peer. -// This is a non-blocking connection. You know the connection is successful when IsConnected() returns true -// or receive gets a packet with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. If the connection is not -// successful, such as rejected connection or no response then neither of these things will happen. -// Requires that you first call Initialize -// -// Parameters: -// host: Either a dotted IP address or a domain name -// remotePort: Which port to connect to on the remote machine. -// passwordData: A data block that must match the data block on the server. This can be just a password, or can be a stream of data -// passwordDataLength: The length in bytes of passwordData -// -// Returns: -// True on successful initiation. False on incorrect parameters, internal error, or too many existing peers -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -ConnectionAttemptResult RakPeer::Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime ) -{ - // If endThreads is true here you didn't call Startup() first. - if ( host == 0 || endThreads || connectionSocketIndex>=socketList.Size() ) - return INVALID_PARAMETER; - - RakAssert(remotePort!=0); - - connectionSocketIndex=GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - - if (passwordDataLength>255) - passwordDataLength=255; - - if (passwordData==0) - passwordDataLength=0; - - // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? - // It won't overflow at least because outgoingPasswordLength is an unsigned char -// if (passwordDataLength>0) -// memcpy(outgoingPassword, passwordData, passwordDataLength); -// outgoingPasswordLength=(unsigned char) passwordDataLength; - - // 04/02/09 - Can't remember why I disabled connecting to self, but it seems to work - // Connecting to ourselves in the same instance of the program? -// if ( ( strcmp( host, "127.0.0.1" ) == 0 || strcmp( host, "0.0.0.0" ) == 0 ) && remotePort == mySystemAddress[0].port ) -// return false; - - return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, connectionSocketIndex, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -ConnectionAttemptResult RakPeer::ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSocket2* socket, PublicKey *publicKey, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime) -{ - if ( host == 0 || endThreads || socket == 0 ) - return INVALID_PARAMETER; - - if (passwordDataLength>255) - passwordDataLength=255; - - if (passwordData==0) - passwordDataLength=0; - - return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, 0, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime, socket ); - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Stops the network threads and close all connections. Multiple calls are ok. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::Shutdown( unsigned int blockDuration, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority ) -{ - unsigned i,j; - bool anyActive; - MafiaNet::TimeMS startWaitingTime; -// SystemAddress systemAddress; - MafiaNet::TimeMS time; - //unsigned short systemListSize = remoteSystemListSize; // This is done for threading reasons - unsigned int systemListSize = maximumNumberOfPeers; - - ++shutdownGeneration; - - if ( blockDuration > 0 ) - { - for ( i = 0; i < systemListSize; i++ ) - { - // remoteSystemList in user thread - if (remoteSystemList[i].isActive) - NotifyAndFlagForShutdown(remoteSystemList[i].systemAddress, false, orderingChannel, disconnectionNotificationPriority); - } - - time = MafiaNet::GetTimeMS(); - startWaitingTime = time; - // Nothing else advances a manually updated peer (or its clock, under a - // test's virtual time): flush the notifications once and stop. - if (manualUpdate) - RunManualUpdate(); - while ( !manualUpdate && time - startWaitingTime < blockDuration ) - { - anyActive=false; - for (j=0; j < systemListSize; j++) - { - // remoteSystemList in user thread - if (remoteSystemList[j].isActive) - { - anyActive=true; - break; - } - } - - // If this system is out of packets to send, then stop waiting - if ( anyActive==false ) - break; - - // This will probably cause the update thread to run which will probably - // send the disconnection notification - - RakSleep(15); - time = MafiaNet::GetTimeMS(); - } - } - // Plugins tear down in OnRakPeerShutdown while the update thread may still be running; stop - // offering them messages there first, so no hook is mid-call or starts after this point. - SetNetworkThreadReceiveStopped(true); - - for (i=0; i < pluginListTS.Size(); i++) - { - pluginListTS[i]->OnRakPeerShutdown(); - } - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginListNTS[i]->OnRakPeerShutdown(); - } - - quitAndDataEvents.SetEvent(); - - endThreads = true; - -// MafiaNet::TimeMS timeout; -#if RAKPEER_USER_THREADED!=1 - - for (i=0; i < socketList.Size(); i++) - { - if (socketList[i]->IsBerkleySocket()) - { - ((RNS2_Berkley *)socketList[i])->SignalStopRecvPollingThread(); - } - } - - /* - // Get recvfrom to unblock - for (i=0; i < socketList.Size(); i++) - { - if (SocketLayer::SendTo(socketList[i], (const char*) &i,1,socketList[i]->GetBoundAddress(), _FILE_AND_LINE_)!=0) - break; - } - */ - - while ( isMainLoopThreadActive ) - { - RakSleep(15); - } - - activeSystemListSize = 0; - - /* - timeout = MafiaNet::GetTimeMS()+1000; - while ( isRecvFromLoopThreadActive.GetValue()>0 && MafiaNet::GetTimeMS()GetBoundAddress(), _FILE_AND_LINE_); - } - - RakSleep(30); - } - */ - - for (i=0; i < socketList.Size(); i++) - { - if (socketList[i]->IsBerkleySocket()) - { - ((RNS2_Berkley *)socketList[i])->BlockOnStopRecvPollingThread(); - } - } - - -#endif // RAKPEER_USER_THREADED!=1 - -// char c=0; -// unsigned int socketIndex; - // remoteSystemList in Single thread - for ( i = 0; i < systemListSize; i++ ) - { - // Reserve this reliability layer for ourselves - remoteSystemList[ i ].isActive = false; - - // Remove any remaining packets - RakAssert(remoteSystemList[ i ].MTUSize <= MAXIMUM_MTU_SIZE); - remoteSystemList[ i ].reliabilityLayer.Reset(false, remoteSystemList[ i ].MTUSize, false); - remoteSystemList[ i ].rakNetSocket = 0; - ClearDisconnectReason(&remoteSystemList[ i ]); - ClearSessionConfig(&remoteSystemList[ i ]); - } - - - // Setting maximumNumberOfPeers to 0 allows remoteSystemList to be reallocated in Initialize. - // Setting remoteSystemListSize prevents threads from accessing the reliability layer - maximumNumberOfPeers = 0; - //remoteSystemListSize = 0; - - // Free any packets the user didn't deallocate - packetReturnMutex.Lock(); - for (i=0; i < packetReturnQueue.Size(); i++) - DeallocatePacket(packetReturnQueue[i]); - packetReturnQueue.Clear(_FILE_AND_LINE_); - packetReturnMutex.Unlock(); - // Packets still out live in the pool's pages: a Shutdown() from inside Receive's dispatch, - // or from code handling a packet Receive returned, must not free the packet under it. The - // pages are reused by the next session and freed by a later Shutdown() or the destructor. - packetAllocationPoolMutex.Lock(); - if (packetsOutstanding == 0) - packetAllocationPool.Clear(_FILE_AND_LINE_); - packetAllocationPoolMutex.Unlock(); - - /* - if (isRecvFromLoopThreadActive.GetValue()>0) - { - timeout = MafiaNet::GetTimeMS()+1000; - while ( isRecvFromLoopThreadActive.GetValue()>0 && MafiaNet::GetTimeMS() addresses; - DataStructures::List guids; - GetSystemList(addresses, guids); - if (remoteSystems) - { - unsigned short i; - for (i=0; i < *numberOfSystems && i < addresses.Size(); i++) - remoteSystems[i]=addresses[i]; - *numberOfSystems=i; - } - else - { - *numberOfSystems=(unsigned short) addresses.Size(); - } - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::GetNextSendReceipt(void) -{ - sendReceiptSerialMutex.Lock(); - uint32_t retVal = sendReceiptSerial; - sendReceiptSerialMutex.Unlock(); - return retVal; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::IncrementNextSendReceipt(void) -{ - sendReceiptSerialMutex.Lock(); - uint32_t returned = sendReceiptSerial; - if (++sendReceiptSerial==0) - sendReceiptSerial=1; - sendReceiptSerialMutex.Unlock(); - return returned; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sends a block of data to the specified system that you are connected to. -// This function only works while the client is connected (Use the Connect function). -// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM -// -// Parameters: -// data: The block of data to send -// length: The size in bytes of the data to send -// bitStream: The bitstream to send -// priority: What priority level to send on. -// reliability: How reliability to send this data -// orderingChannel: When using ordered or sequenced packets, what channel to order these on. -// - Packets are only ordered relative to other packets on the same stream -// systemAddress: Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none -// broadcast: True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. -// Returns: -// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// A peer mid-session-handshake has not been reported to the application, so the application must not be -// able to address it. Checked here rather than in SendImmediate because the handshake's own messages go -// through SendImmediate directly and must still reach that peer. -bool RakPeer::IsExchangingSessionData( const AddressOrGUID systemIdentifier ) -{ - if (remoteSystemList==0) - return false; - - int index; - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); - else - index = GetIndexFromGuid(systemIdentifier.rakNetGuid); - - return index!=-1 && remoteSystemList[index].isActive && - remoteSystemList[index].connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::Send( const char *data, const int length, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) -{ -#ifdef _DEBUG - RakAssert( data && length > 0 ); -#endif - RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); - RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); - RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); - - if ( data == 0 || length < 0 ) - return 0; - - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - if ( broadcast == false && systemIdentifier.IsUndefined()) - return 0; - - if ( broadcast == false && IsExchangingSessionData(systemIdentifier) ) - return 0; - - uint32_t usedSendReceipt; - if (forceReceiptNumber!=0) - usedSendReceipt=forceReceiptNumber; - else - usedSendReceipt=IncrementNextSendReceipt(); - - if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) - { - SendLoopback(data,length); - - if (reliability>=MafiaNet::Reliability::UnreliableWithAckReceipt) - { - char buff[5]; - buff[0]=ID_SND_RECEIPT_ACKED; - sendReceiptSerialMutex.Lock(); - memcpy(buff+1, &sendReceiptSerial, 4); - sendReceiptSerialMutex.Unlock(); - SendLoopback( buff, 5 ); - } - - return usedSendReceipt; - } - - SendBuffered(data, length*8, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); - - return usedSendReceipt; -} - -void RakPeer::SendLoopback( const char *data, const int length ) -{ - if ( data == 0 || length < 0 ) - return; - - Packet *packet = AllocPacket(length, _FILE_AND_LINE_); - memcpy(packet->data, data, length); - packet->systemAddress = GetLoopbackAddress(); - packet->guid=myGuid; - PushBackPacket(packet, false); -} - -uint32_t RakPeer::Send( const MafiaNet::BitStream * bitStream, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) -{ -#ifdef _DEBUG - RakAssert( bitStream->GetNumberOfBytesUsed() > 0 ); -#endif - - RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); - RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); - RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); - - if ( bitStream->GetNumberOfBytesUsed() == 0 ) - return 0; - - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - if ( broadcast == false && systemIdentifier.IsUndefined() ) - return 0; - - if ( broadcast == false && IsExchangingSessionData(systemIdentifier) ) - return 0; - - uint32_t usedSendReceipt; - if (forceReceiptNumber!=0) - usedSendReceipt=forceReceiptNumber; - else - usedSendReceipt=IncrementNextSendReceipt(); - - if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) - { - SendLoopback((const char*) bitStream->GetData(),bitStream->GetNumberOfBytesUsed()); - if (reliability>=MafiaNet::Reliability::UnreliableWithAckReceipt) - { - char buff[5]; - buff[0]=ID_SND_RECEIPT_ACKED; - sendReceiptSerialMutex.Lock(); - memcpy(buff+1, &sendReceiptSerial,4); - sendReceiptSerialMutex.Unlock(); - SendLoopback( buff, 5 ); - } - return usedSendReceipt; - } - - // Sends need to be buffered and processed in the update thread because the systemAddress associated with the reliability layer can change, - // from that thread, resulting in a send to the wrong player! While I could mutex the systemAddress, that is much slower than doing this - SendBuffered((const char*)bitStream->GetData(), bitStream->GetNumberOfBitsUsed(), priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); - - - return usedSendReceipt; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Sends multiple blocks of data, concatenating them automatically. -// -// This is equivalent to: -// MafiaNet::BitStream bs; -// bs.WriteAlignedBytes(block1, blockLength1); -// bs.WriteAlignedBytes(block2, blockLength2); -// bs.WriteAlignedBytes(block3, blockLength3); -// Send(&bs, ...) -// -// This function only works while connected -// \param[in] data An array of pointers to blocks of data -// \param[in] lengths An array of integers indicating the length of each block of data -// \param[in] numParameters Length of the arrays data and lengths -// \param[in] priority What priority level to send on. See packet_priority.h -// \param[in] reliability How reliability to send this data. See packet_priority.h -// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream -// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none -// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. -// \return False if we are not connected to the specified recipient. True otherwise -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -uint32_t RakPeer::SendList( const char **data, const int *lengths, const int numParameters, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) -{ -#ifdef _DEBUG - RakAssert( data ); -#endif - - if ( data == 0 || lengths == 0 ) - return 0; - - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - if (numParameters==0) - return 0; - - if (lengths==0) - return 0; - - if ( broadcast == false && systemIdentifier.IsUndefined() ) - return 0; - - if ( broadcast == false && IsExchangingSessionData(systemIdentifier) ) - return 0; - - uint32_t usedSendReceipt; - if (forceReceiptNumber!=0) - usedSendReceipt=forceReceiptNumber; - else - usedSendReceipt=IncrementNextSendReceipt(); - - SendBufferedList(data, lengths, numParameters, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); - - return usedSendReceipt; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Gets a packet from the incoming packet queue. Use DeallocatePacket to deallocate the packet after you are done with it. -// Check the Packet struct at the top of CoreNetworkStructures.h for the format of the struct -// -// Returns: -// 0 if no packets are waiting to be handled, otherwise an allocated packet -// If the client is not active this will also return 0, as all waiting packets are flushed when the client is Disconnected -// This also updates all memory blocks associated with synchronized memory and distributed objects -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -Packet* RakPeer::Receive( void ) -{ - if ( !( IsActive() ) ) - return 0; - - MafiaNet::Packet *packet; -// Packet **threadPacket; - PluginReceiveResult pluginResult; - - int offset; - unsigned int i; - - // User should call RunUpdateCycle and RunRecvFromOnce to do this commented code - /* -#if RAKPEER_SINGLE_THREADED==1 - RakPeer::RecvFromStruct *recvFromStruct; - for (i=0; i < socketList.Size(); i++) - { - for(;;) - { - recvFromStruct=bufferedPackets.Allocate( _FILE_AND_LINE_ ); - recvFromStruct->s=socketList[i]->s; - recvFromStruct->extraSocketOptions=socketList[i]->extraSocketOptions; - SocketLayer::RecvFromBlocking( - recvFromStruct->s, this, recvFromStruct->extraSocketOptions, recvFromStruct->data, &recvFromStruct->bytesRead, &recvFromStruct->systemAddress, &recvFromStruct->timeRead); - if (recvFromStruct->bytesRead<=0) - { - bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); - break; - } - else - { - RakAssert(recvFromStruct->systemAddress.GetPort()); - bufferedPackets.Push(recvFromStruct); - } - } - } - - BitStream updateBitStream( MAXIMUM_MTU_SIZE -#if LIBCAT_SECURITY==1 - + cat::AuthenticatedEncryption::OVERHEAD_BYTES -#endif - ); - RunUpdateCycle(0, 0, updateBitStream); -#endif - */ - - for (i=0; i < pluginListTS.Size(); i++) - { - pluginListTS[i]->Update(); - } - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginListNTS[i]->Update(); - } - - do - { - packetReturnMutex.Lock(); - if (packetReturnQueue.IsEmpty()) - packet=0; - else - packet = packetReturnQueue.Pop(); - packetReturnMutex.Unlock(); - if (packet==0) - return 0; - -// unsigned char msgId; - if ( ( packet->length >= sizeof(unsigned char) + sizeof(MafiaNet::Time ) ) && - ( (unsigned char) packet->data[ 0 ] == ID_TIMESTAMP ) ) - { - offset = sizeof(unsigned char); - ShiftIncomingTimestamp( packet->data + offset, packet->systemAddress ); -// msgId=packet->data[sizeof(unsigned char) + sizeof( MafiaNet::Time )]; - } -// else - // msgId=packet->data[0]; - - // Some locally generated packets need to be processed by plugins, for example ID_FCM2_NEW_HOST - // The plugin itself should intercept these messages generated remotely -// if (packet->wasGeneratedLocally) -// return packet; - - - // A callback may shut the peer down. The plugins have been reset by then and the packet - // belongs to a closed session, so it goes no further and Receive returns nothing. - const unsigned int generation = shutdownGeneration; - - CallPluginCallbacks(pluginListTS, packet); - CallPluginCallbacks(pluginListNTS, packet); - if (shutdownGeneration != generation) - { - DeallocatePacket( packet ); - return 0; - } - - for (i=0; i < pluginListTS.Size(); i++) - { - pluginResult=pluginListTS[i]->OnReceive(packet); - if (shutdownGeneration != generation) - { - if (pluginResult!=RR_STOP_PROCESSING) - DeallocatePacket( packet ); - return 0; - } - if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) - { - DeallocatePacket( packet ); - packet=0; // Will do the loop again and get another packet - break; // break out of the enclosing for - } - else if (pluginResult==RR_STOP_PROCESSING) - { - packet=0; - break; - } - } - - for (i=0; i < pluginListNTS.Size(); i++) - { - pluginResult=pluginListNTS[i]->OnReceive(packet); - if (shutdownGeneration != generation) - { - if (pluginResult!=RR_STOP_PROCESSING) - DeallocatePacket( packet ); - return 0; - } - if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) - { - DeallocatePacket( packet ); - packet=0; // Will do the loop again and get another packet - break; // break out of the enclosing for - } - else if (pluginResult==RR_STOP_PROCESSING) - { - packet=0; - break; - } - } - - } while(packet==0); - -#ifdef _DEBUG - RakAssert( packet->data ); -#endif - - return packet; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Call this to deallocate a packet returned by Receive -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DeallocatePacket( Packet *packet ) -{ - if ( packet == 0 ) - return; - - if (packet->deleteData) - { - rakFree_Ex(packet->data, _FILE_AND_LINE_ ); - packet->~Packet(); - packetAllocationPoolMutex.Lock(); - packetAllocationPool.Release(packet,_FILE_AND_LINE_); - --packetsOutstanding; - packetAllocationPoolMutex.Unlock(); - } - else - { - rakFree_Ex(packet, _FILE_AND_LINE_ ); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Return the total number of connections we are allowed -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetMaximumNumberOfPeers( void ) const -{ - return maximumNumberOfPeers; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). -// -// Parameters: -// target: Which connection to close -// sendDisconnectionNotification: True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. -// channel: If blockDuration > 0, the disconnect packet will be sent on this channel -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority, const MafiaNet::BitStream *reasonData ) -{ - /* - // This only be called from the user thread, for the user shutting down. - // From the network thread, this should occur because of ID_DISCONNECTION_NOTIFICATION and ID_CONNECTION_LOST - unsigned j; - for (j=0; j < messageHandlerList.Size(); j++) - { - messageHandlerList[j]->OnClosedConnection( - target.systemAddress==UNASSIGNED_SYSTEM_ADDRESS ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress, - target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid, - LCR_CLOSED_BY_USER); - } - */ - - const SystemAddress address = (target.systemAddress == UNASSIGNED_SYSTEM_ADDRESS) ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress; - int remoteSystemListIndex = GetIndexFromSystemAddress(address); - - // Resolve the socket to close on WITHOUT assuming a valid slot index. - // GetIndexFromSystemAddress returns -1 when the target isn't in the list; never - // coerce that to 0 — reading remoteSystemList[0] would crash if the list is - // unallocated, or target an unrelated peer's slot. When the index is valid use - // that slot's socket (it may itself be null during rapid connect/disconnect - // churn). Either way, fall back to the primary socket — the same pattern used by - // the BCS_CLOSE_CONNECTION path below. With no socket at all there is nothing to - // close, so bail out. - RakNetSocket2 *closeSocket = (remoteSystemListIndex != -1) ? remoteSystemList[remoteSystemListIndex].rakNetSocket : nullptr; - if (closeSocket == nullptr && socketList.Size() > 0) - closeSocket = socketList[0]; - if (closeSocket == nullptr) - return; - CloseConnectionInternal2(target, sendDisconnectionNotification, false, orderingChannel, disconnectionNotificationPriority, *closeSocket, reasonData); - - // 12/14/09 Return ID_CONNECTION_LOST when calling CloseConnection with sendDisconnectionNotification==false, elsewise it is never returned - if (sendDisconnectionNotification==false && GetConnectionState(target)==IS_CONNECTED) - { - Packet *packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection - packet->guid = target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid; - packet->systemAddress = address; - packet->systemAddress.systemIndex = static_cast(remoteSystemListIndex == -1 ? 0 : remoteSystemListIndex); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - packet->wasGeneratedLocally=true; // else processed twice - AddPacketToProducer(packet); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Cancel a pending connection attempt -// If we are already connected, the connection stays open -// \param[in] target Which system to cancel -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::CancelConnectionAttempt( const SystemAddress target ) -{ - unsigned int i; - - // Cancel pending connection attempt, if there is one - i=0; - requestedConnectionQueueMutex.Lock(); - while (i < requestedConnectionQueue.Size()) - { - if (requestedConnectionQueue[i]->systemAddress==target) - { -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Deleting requestedConnectionQueue %i client_handshake %x\n", i, requestedConnectionQueue[ i ]->client_handshake); - MafiaNet::OP_DELETE(requestedConnectionQueue[i]->client_handshake, _FILE_AND_LINE_ ); -#endif - MafiaNet::OP_DELETE(requestedConnectionQueue[i], _FILE_AND_LINE_ ); - requestedConnectionQueue.RemoveAtIndex(i); - break; - } - else - i++; - } - requestedConnectionQueueMutex.Unlock(); - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -ConnectionState RakPeer::GetConnectionState(const AddressOrGUID systemIdentifier) -{ - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - unsigned int i=0; - requestedConnectionQueueMutex.Lock(); - for (; i < requestedConnectionQueue.Size(); i++) - { - if (requestedConnectionQueue[i]->systemAddress==systemIdentifier.systemAddress) - { - requestedConnectionQueueMutex.Unlock(); - return IS_PENDING; - } - } - requestedConnectionQueueMutex.Unlock(); - } - - int index; - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); - } - else - { - index = GetIndexFromGuid(systemIdentifier.rakNetGuid); - } - - if (index==-1) - return IS_NOT_CONNECTED; - - if (remoteSystemList[index].isActive==false) - return IS_DISCONNECTED; - - switch (remoteSystemList[index].connectMode) - { - case RemoteSystemStruct::DISCONNECT_ASAP: - return IS_DISCONNECTING; - case RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY: - return IS_SILENTLY_DISCONNECTING; - case RemoteSystemStruct::DISCONNECT_ON_NO_ACK: - return IS_DISCONNECTING; - case RemoteSystemStruct::REQUESTED_CONNECTION: - return IS_CONNECTING; - case RemoteSystemStruct::HANDLING_CONNECTION_REQUEST: - return IS_CONNECTING; - case RemoteSystemStruct::UNVERIFIED_SENDER: - return IS_CONNECTING; - case RemoteSystemStruct::EXCHANGING_SESSION_DATA: - // Transport is up but the session handshake has not completed, so the application has not been - // told this connection exists yet. - return IS_CONNECTING; - case RemoteSystemStruct::CONNECTED: - return IS_CONNECTED; - default: - return IS_NOT_CONNECTED; - } -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Given a systemAddress, returns an index from 0 to the maximum number of players allowed - 1. -// -// Parameters -// systemAddress - The systemAddress to search for -// -// Returns -// An integer from 0 to the maximum number of peers -1, or -1 if that player is not found -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress ) const -{ - return GetIndexFromSystemAddress(systemAddress, false); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// This function is only useful for looping through all players. -// -// Parameters -// index - an integer between 0 and the maximum number of players allowed - 1. -// -// Returns -// A valid systemAddress or UNASSIGNED_SYSTEM_ADDRESS if no such player at that index -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetSystemAddressFromIndex( unsigned int index ) -{ - // remoteSystemList in user thread - //if ( index >= 0 && index < remoteSystemListSize ) - if ( index < maximumNumberOfPeers ) - if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail - return remoteSystemList[ index ].systemAddress; - - return UNASSIGNED_SYSTEM_ADDRESS; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Same as GetSystemAddressFromIndex but returns RakNetGUID -// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. -// \return The RakNetGUID -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakNetGUID RakPeer::GetGUIDFromIndex( unsigned int index ) -{ - // remoteSystemList in user thread - //if ( index >= 0 && index < remoteSystemListSize ) - if ( index < maximumNumberOfPeers ) - if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail - return remoteSystemList[ index ].guid; - - return UNASSIGNED_RAKNET_GUID; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient -// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system -// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the list will match the size of the \a guids list. -// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const -{ - addresses.Clear(false, _FILE_AND_LINE_); - guids.Clear(false, _FILE_AND_LINE_); - - if ( remoteSystemList == 0 || endThreads == true ) - return; - - unsigned int i; - for (i=0; i < activeSystemListSize; i++) - { - if ((activeSystemList[i])->isActive && - (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED) - { - addresses.Push((activeSystemList[i])->systemAddress, _FILE_AND_LINE_ ); - guids.Push((activeSystemList[i])->guid, _FILE_AND_LINE_ ); - } - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Bans an IP from connecting. Banned IPs persist between connections. -// -// Parameters -// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban -// All IP addresses starting with 128.0.0 -// milliseconds - how many ms for a temporary ban. Use 0 for a permanent ban -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AddToBanList( const char *IP, MafiaNet::TimeMS milliseconds ) -{ - unsigned index; - MafiaNet::TimeMS time = MafiaNet::GetTimeMS(); - - if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) - return ; - - // If this guy is already in the ban list, do nothing - index = 0; - - banListMutex.Lock(); - - for ( ; index < banList.Size(); index++ ) - { - if ( strcmp( IP, banList[ index ]->IP ) == 0 ) - { - // Already in the ban list. Just update the time - if (milliseconds==0) - banList[ index ]->timeout=0; // Infinite - else - banList[ index ]->timeout=time+milliseconds; - banListMutex.Unlock(); - return; - } - } - - banListMutex.Unlock(); - - BanStruct *banStruct = MafiaNet::OP_NEW( _FILE_AND_LINE_ ); - banStruct->IP = (char*) rakMalloc_Ex( 16, _FILE_AND_LINE_ ); - if (milliseconds==0) - banStruct->timeout=0; // Infinite - else - banStruct->timeout=time+milliseconds; - strcpy_s( banStruct->IP, 16, IP ); - banListMutex.Lock(); - banList.Insert( banStruct, _FILE_AND_LINE_ ); - banListMutex.Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Allows a previously banned IP to connect. -// -// Parameters -// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban -// All IP addresses starting with 128.0.0 -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RemoveFromBanList( const char *IP ) -{ - unsigned index; - BanStruct *temp; - - if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) - return ; - - index = 0; - temp=0; - - banListMutex.Lock(); - - for ( ; index < banList.Size(); index++ ) - { - if ( strcmp( IP, banList[ index ]->IP ) == 0 ) - { - temp = banList[ index ]; - banList[ index ] = banList[ banList.Size() - 1 ]; - banList.RemoveAtIndex( banList.Size() - 1 ); - break; - } - } - - banListMutex.Unlock(); - - if (temp) - { - rakFree_Ex(temp->IP, _FILE_AND_LINE_ ); - MafiaNet::OP_DELETE(temp, _FILE_AND_LINE_); - } - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Allows all previously banned IPs to connect. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearBanList( void ) -{ - unsigned index; - index = 0; - banListMutex.Lock(); - - for ( ; index < banList.Size(); index++ ) - { - rakFree_Ex(banList[ index ]->IP, _FILE_AND_LINE_ ); - MafiaNet::OP_DELETE(banList[ index ], _FILE_AND_LINE_); - } - - banList.Clear(false, _FILE_AND_LINE_); - - banListMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetLimitIPConnectionFrequency(bool b) -{ - limitConnectionFrequencyFromTheSameIP=b; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Determines if a particular IP is banned. -// -// Parameters -// IP - Complete dotted IP address -// -// Returns -// True if IP matches any IPs in the ban list, accounting for any wildcards. -// False otherwise. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsBanned( const char *IP ) -{ - unsigned banListIndex, characterIndex; - MafiaNet::TimeMS time; - BanStruct *temp; - - if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) - return false; - - banListIndex = 0; - - if ( banList.Size() == 0 ) - return false; // Skip the mutex if possible - - time = MafiaNet::GetTimeMS(); - - banListMutex.Lock(); - - while ( banListIndex < banList.Size() ) - { - if (banList[ banListIndex ]->timeout>0 && banList[ banListIndex ]->timeoutIP, _FILE_AND_LINE_ ); - MafiaNet::OP_DELETE(temp, _FILE_AND_LINE_); - } - else - { - characterIndex = 0; - - for(;;) - { - if ( banList[ banListIndex ]->IP[ characterIndex ] == IP[ characterIndex ] ) - { - // Equal characters - - if ( IP[ characterIndex ] == 0 ) - { - banListMutex.Unlock(); - // End of the string and the strings match - - return true; - } - - characterIndex++; - } - - else - { - if ( banList[ banListIndex ]->IP[ characterIndex ] == 0 || IP[ characterIndex ] == 0 ) - { - // End of one of the strings - break; - } - - // Characters do not match - if ( banList[ banListIndex ]->IP[ characterIndex ] == '*' ) - { - banListMutex.Unlock(); - - // Domain is banned. - return true; - } - - // Characters do not match and it is not a * - break; - } - } - - banListIndex++; - } - } - - banListMutex.Unlock(); - - // No match found. - return false; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Send a ping to the specified connected system. -// -// Parameters: -// target - who to ping -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::Ping( const SystemAddress target ) -{ - PingInternal(target, false, MafiaNet::Reliability::Unreliable); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Send a ping to the specified unconnected system. -// The remote system, if it is Initialized, will respond with ID_UNCONNECTED_PONG. -// The final ping time will be encoded in the following sizeof(MafiaNet::TimeMS) bytes. (Default is 4 bytes - See __GET_TIME_64BIT in types.h -// -// Parameters: -// host: Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. -// remotePort: Which port to connect to on the remote machine. -// onlyReplyOnAcceptingConnections: Only request a reply if the remote system has open connections -// connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex ) -{ - if ( host == 0 ) - return false; - - // If this assert hits then Startup wasn't called or the call failed. - RakAssert(connectionSocketIndex < socketList.Size()); - -// if ( IsActive() == false ) -// return; - - MafiaNet::BitStream bitStream( sizeof(unsigned char) + sizeof(MafiaNet::Time) ); - if ( onlyReplyOnAcceptingConnections ) - bitStream.Write((MessageID)ID_UNCONNECTED_PING_OPEN_CONNECTIONS); - else - bitStream.Write((MessageID)ID_UNCONNECTED_PING); - - bitStream.Write(MafiaNet::GetTime()); - - bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - - bitStream.Write(GetMyGUID()); - - // No timestamp for 255.255.255.255 - unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - /* - - SystemAddress systemAddress; - systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); - systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); - - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( socketList[realIndex], (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - */ - - RNS2_SendParameters bsp; - bsp.data = (char*) bitStream.GetData() ; - bsp.length = bitStream.GetNumberOfBytesUsed(); - bsp.systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); - if (bsp.systemAddress==UNASSIGNED_SYSTEM_ADDRESS) - return false; - bsp.systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), bsp.systemAddress); - socketList[realIndex]->Send(&bsp, _FILE_AND_LINE_); - - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the average of all ping times read for a specified target -// -// Parameters: -// target - whose time to read -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetAveragePing( const AddressOrGUID systemIdentifier ) -{ - int sum, quantity; - RemoteSystemStruct *remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); - - if ( remoteSystem == 0 ) - return -1; - - for ( sum = 0, quantity = 0; quantity < PING_TIMES_ARRAY_SIZE; quantity++ ) - { - if ( remoteSystem->pingAndClockDifferential[ quantity ].pingTime == 65535 ) - break; - else - sum += remoteSystem->pingAndClockDifferential[ quantity ].pingTime; - } - - if ( quantity > 0 ) - return sum / quantity; - else - return -1; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the last ping time read for the specific player or -1 if none read yet -// -// Parameters: -// target - whose time to read -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetLastPing( const AddressOrGUID systemIdentifier ) const -{ - RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); - - if ( remoteSystem == 0 ) - return -1; - -// return (int)(remoteSystem->reliabilityLayer.GetAckPing()/(MafiaNet::TimeUS)1000); - - if ( remoteSystem->pingAndClockDifferentialWriteIndex == 0 ) - return remoteSystem->pingAndClockDifferential[ PING_TIMES_ARRAY_SIZE - 1 ].pingTime; - else - return remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex - 1 ].pingTime; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the lowest ping time read or -1 if none read yet -// -// Parameters: -// target - whose time to read -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetLowestPing( const AddressOrGUID systemIdentifier ) const -{ - RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); - - if ( remoteSystem == 0 ) - return -1; - - return remoteSystem->lowestPing; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Ping the remote systems every so often. This is off by default -// This will work anytime -// -// Parameters: -// doPing - True to start occasional pings. False to stop them. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetOccasionalPing( bool doPing ) -{ - occasionalPing = doPing; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -/// Return the clock difference between your system and the specified system -/// Subtract the time from a time returned by the remote system to get that time relative to your own system -/// Returns 0 if the system is unknown -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -MafiaNet::Time RakPeer::GetClockDifferential( const AddressOrGUID systemIdentifier ) -{ - RemoteSystemStruct *remoteSystem = GetRemoteSystem(systemIdentifier, false, false); - if (remoteSystem == 0) - return 0; - return GetClockDifferentialInt(remoteSystem); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -MafiaNet::Time RakPeer::GetClockDifferentialInt(RemoteSystemStruct *remoteSystem) const -{ - int counter, lowestPingSoFar; - MafiaNet::Time clockDifferential; - - lowestPingSoFar = 65535; - - clockDifferential = 0; - - for ( counter = 0; counter < PING_TIMES_ARRAY_SIZE; counter++ ) - { - if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime == 65535 ) - break; - - if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime < lowestPingSoFar ) - { - clockDifferential = remoteSystem->pingAndClockDifferential[ counter ].clockDifferential; - lowestPingSoFar = remoteSystem->pingAndClockDifferential[ counter ].pingTime; - } - } - - return clockDifferential; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Length should be under 400 bytes, as a security measure against flood attacks -// Sets the data to send with an (LAN server discovery) /(offline ping) response -// See the Ping sample project for how this is used. -// data: a block of data to store, or 0 for none -// length: The length of data in bytes, or 0 for none -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetOfflinePingResponse( const char *data, const unsigned int length ) -{ - RakAssert(length < 400); - - rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); - offlinePingResponse.Reset(); - - if ( data && length > 0 ) - offlinePingResponse.Write( data, length ); - - rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns pointers to a copy of the data passed to SetOfflinePingResponse -// \param[out] data A pointer to a copy of the data passed to \a SetOfflinePingResponse() -// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() -// \sa SetOfflinePingResponse -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetOfflinePingResponse( char **data, unsigned int *length ) -{ - rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); - *data = (char*) offlinePingResponse.GetData(); - *length = (int) offlinePingResponse.GetNumberOfBytesUsed(); - rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Sets the payload this peer offers during the session handshake. Opaque to MafiaNet. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetSessionConfig( const char *data, unsigned int length ) -{ - RakAssert(length <= MAXIMUM_SESSION_CONFIG_SIZE); - if (length > MAXIMUM_SESSION_CONFIG_SIZE) - length = MAXIMUM_SESSION_CONFIG_SIZE; - - rakPeerMutexes[ sessionConfig_Mutex ].Lock(); - sessionConfig.Reset(); - - if ( data && length > 0 ) - sessionConfig.Write( data, length ); - - rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetSessionConfig( char **data, unsigned int *length ) -{ - rakPeerMutexes[ sessionConfig_Mutex ].Lock(); - *data = (char*) sessionConfig.GetData(); - *length = (unsigned int) sessionConfig.GetNumberOfBytesUsed(); - rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -const char *RakPeer::GetRemoteSessionConfig( const AddressOrGUID systemIdentifier, unsigned int *length ) -{ - if (length) - *length = 0; - - int index; - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); - else - index = GetIndexFromGuid(systemIdentifier.rakNetGuid); - - if (index==-1 || remoteSystemList==0 || remoteSystemList[index].isActive==false) - return 0; - - if (length) - *length = remoteSystemList[index].remoteSessionConfigLength; - return (const char*) remoteSystemList[index].remoteSessionConfigData; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetSessionConfigInteractive( bool interactive ) -{ - sessionConfigInteractive = interactive; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AcceptSession( const AddressOrGUID systemIdentifier, const char *data, unsigned int length ) -{ - RakAssert(length <= MAXIMUM_SESSION_CONFIG_SIZE); - if (length > MAXIMUM_SESSION_CONFIG_SIZE) - length = MAXIMUM_SESSION_CONFIG_SIZE; - QueueSessionDecision(systemIdentifier, true, data, length); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RejectSession( const AddressOrGUID systemIdentifier, const char *reason ) -{ - unsigned int reasonLength = (reason && reason[0]) ? (unsigned int) strlen(reason) : 0; - // Bounded like every other payload that crosses the handshake. A reason is meant to be a short - // string, so an oversized one is a caller mistake rather than something to transmit. - RakAssert(reasonLength <= MAXIMUM_SESSION_CONFIG_SIZE); - if (reasonLength > MAXIMUM_SESSION_CONFIG_SIZE) - reasonLength = MAXIMUM_SESSION_CONFIG_SIZE; - QueueSessionDecision(systemIdentifier, false, reason, reasonLength); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetMaximumPendingSessions( unsigned short total, unsigned short perAddress ) -{ - maximumPendingSessions = total; - maximumPendingSessionsPerAddress = total != 0 ? perAddress : 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetSessionTimeout( MafiaNet::TimeMS timeMS ) -{ - sessionTimeout = timeMS; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendSessionStatus( const AddressOrGUID systemIdentifier, const char *data, unsigned int length ) -{ - RakAssert(length <= MAXIMUM_SESSION_CONFIG_SIZE); - if (length > MAXIMUM_SESSION_CONFIG_SIZE) - length = MAXIMUM_SESSION_CONFIG_SIZE; - QueueSessionCommand(systemIdentifier, BufferedCommandStruct::BCS_SESSION_STATUS, data, length); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Hand an application's accept/reject over to the network thread. Applying it here would send on the -// connection and mutate connectMode from the user thread, which the rest of RakPeer never does. -void RakPeer::QueueSessionDecision( const AddressOrGUID systemIdentifier, bool accept, const char *data, unsigned int length ) -{ - QueueSessionCommand(systemIdentifier, accept ? BufferedCommandStruct::BCS_SESSION_ACCEPT : BufferedCommandStruct::BCS_SESSION_REJECT, data, length); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::QueueSessionCommand( const AddressOrGUID systemIdentifier, int command, const char *data, unsigned int length ) -{ - BufferedCommandStruct *bcs; - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->command = (decltype(bcs->command)) command; - bcs->systemIdentifier = systemIdentifier; - bcs->numberOfBitsToSend = BYTES_TO_BITS(length); - bcs->broadcast = false; - bcs->connectionMode = RemoteSystemStruct::NO_ACTION; - bcs->orderingChannel = 0; - bcs->priority = MafiaNet::Priority::Immediate; - bcs->reliability = MafiaNet::Reliability::ReliableOrdered; - bcs->data = 0; - if (data && length > 0) - { - bcs->data = (char*) rakMalloc_Ex(length, _FILE_AND_LINE_); - if (bcs->data) - memcpy(bcs->data, data, length); - else - bcs->numberOfBitsToSend = 0; - } - bufferedCommands.Push(bcs); - quitAndDataEvents.SetEvent(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Return the unique SystemAddress that represents you on the the network -// Note that unlike in previous versions, this is a struct and is not sequential -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetInternalID( const SystemAddress systemAddress, const int index ) const -{ - if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) - { - return ipList[index]; - } - else - { - -// SystemAddress returnValue; - RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, false, true ); - if (remoteSystem==0) - return UNASSIGNED_SYSTEM_ADDRESS; - - return remoteSystem->theirInternalSystemAddress[index]; - /* - sockaddr_in sa; - socklen_t len = sizeof(sa); - if (getsockname__(connectionSockets[remoteSystem->connectionSocketIndex], (sockaddr*)&sa, &len)!=0) - return UNASSIGNED_SYSTEM_ADDRESS; - returnValue.port=ntohs(sa.sin_port); - returnValue.binaryAddress=sa.sin_addr.s_addr; - return returnValue; -*/ - - - - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -/// \brief Sets your internal IP address, for platforms that do not support reading it, or to override a value -/// \param[in] systemAddress. The address to set. Use SystemAddress::FromString() if you want to use a dotted string -/// \param[in] index When you have multiple internal IDs, which index to set? -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetInternalID(SystemAddress systemAddress, int index) -{ - RakAssert(index >=0 && index < MAXIMUM_NUMBER_OF_INTERNAL_IDS); - ipList[index]=systemAddress; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Return the unique address identifier that represents you on the the network and is based on your external -// IP / port (the IP / port the specified player uses to communicate with you) -// Note that unlike in previous versions, this is a struct and is not sequential -// -// Parameters: -// target: Which remote system you are referring to for your external ID -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetExternalID( const SystemAddress target ) const -{ - unsigned i; - SystemAddress inactiveExternalId; - - inactiveExternalId=UNASSIGNED_SYSTEM_ADDRESS; - - if (target==UNASSIGNED_SYSTEM_ADDRESS) - return firstExternalID; - - // First check for active connection with this systemAddress - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].systemAddress == target ) - { - if ( remoteSystemList[ i ].isActive ) - return remoteSystemList[ i ].myExternalSystemAddress; - else if (remoteSystemList[ i ].myExternalSystemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - inactiveExternalId=remoteSystemList[ i ].myExternalSystemAddress; - } - } - - return inactiveExternalId; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -const RakNetGUID RakPeer::GetMyGUID(void) const -{ - return myGuid; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetMyBoundAddress(const int socketIndex) -{ - DataStructures::List sockets; - GetSockets( sockets ); - if (sockets.Size()>0) - return sockets[socketIndex]->GetBoundAddress(); - else - return UNASSIGNED_SYSTEM_ADDRESS; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -const RakNetGUID& RakPeer::GetGuidFromSystemAddress( const SystemAddress input ) const -{ - if (input==UNASSIGNED_SYSTEM_ADDRESS) - return myGuid; - - if (input.systemIndex!=(SystemIndex)-1 && input.systemIndexreliabilityLayer.SetTimeoutTime(timeMS); - } -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -MafiaNet::TimeMS RakPeer::GetTimeoutTime( const SystemAddress target ) -{ - if (target==UNASSIGNED_SYSTEM_ADDRESS) - { - return defaultTimeoutTime; - } - else - { - RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( target, false, true ); - - if ( remoteSystem != 0 ) - return remoteSystem->reliabilityLayer.GetTimeoutTime(); - } - return defaultTimeoutTime; -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the current MTU size -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetMTUSize( const SystemAddress target ) const -{ - if (target!=UNASSIGNED_SYSTEM_ADDRESS) - { - RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(target, false, true); - if (rss) - return rss->MTUSize; - } - return defaultMTUSize; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Returns the number of IP addresses we have -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetNumberOfAddresses( void ) -{ - if (IsActive() == false) { - FillIPList(); - } - - unsigned int i = 0; - while (i < MAXIMUM_NUMBER_OF_INTERNAL_IDS && ipList[i] != UNASSIGNED_SYSTEM_ADDRESS) - i++; // count assigned entries until the first unassigned one (end of list) - - return i; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns an IP address at index 0 to GetNumberOfAddresses-1 -// \param[in] index index into the list of IP addresses -// \return The local IP address at this index -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -const char* RakPeer::GetLocalIP( unsigned int index ) -{ - if (IsActive()==false) - { - // Fill out ipList structure - - FillIPList(); - - } - - - static char str[128]; - ipList[index].ToString(false,str,static_cast(128)); - return str; - - - - -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Is this a local IP? -// \param[in] An IP address to check -// \return True if this is one of the IP addresses returned by GetLocalIP -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsLocalIP( const char *ip ) -{ - if (ip==0 || ip[0]==0) - return false; - - // #med - this should also check for "::1" here in IPv6 mode - if (strcmp(ip, "127.0.0.1")==0 || strcmp(ip, "localhost")==0) - return true; - - int num = GetNumberOfAddresses(); - int i; - for (i=0; i < num; i++) - { - if (strcmp(ip, GetLocalIP(i))==0) - return true; - } - - - - - return false; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary -// when connection to servers with multiple IP addresses -// -// Parameters: -// allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AllowConnectionResponseIPMigration( bool allow ) -{ - allowConnectionResponseIPMigration = allow; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Description: -// Sends a message ID_ADVERTISE_SYSTEM to the remote unconnected system. -// This will tell the remote system our external IP outside the LAN, and can be used for NAT punch through -// -// Requires: -// The sender and recipient must already be started via a successful call to Initialize -// -// host: Either a dotted IP address or a domain name -// remotePort: Which port to connect to on the remote machine. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex ) -{ - MafiaNet::BitStream bs; - bs.Write((MessageID)ID_ADVERTISE_SYSTEM); - bs.WriteAlignedBytes((const unsigned char*) data,dataLength); - return SendOutOfBand(host, remotePort, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), connectionSocketIndex ); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. -// ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived -// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. -// Defaults to 0 (never return this notification) -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetSplitMessageProgressInterval(int interval) -{ - RakAssert(interval>=0); - splitMessageProgressInterval=interval; - for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) - remoteSystemList[ i ].reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns what was passed to SetSplitMessageProgressInterval() -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetSplitMessageProgressInterval(void) const -{ - return splitMessageProgressInterval; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Set how long to wait before giving up on sending an unreliable message -// Useful if the network is clogged up. -// Set to 0 or less to never timeout. Defaults to 0. -// timeoutMS How many ms to wait before simply not sending an unreliable message. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetUnreliableTimeout(MafiaNet::TimeMS timeoutMS) -{ - unreliableTimeout=timeoutMS; - for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) - remoteSystemList[ i ].reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Send a message to host, with the IP socket option TTL set to 3 -// This message will not reach the host, but will open the router. -// Used for NAT-Punchthrough -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex ) -{ - char fakeData[2]; - fakeData[0]=0; - fakeData[1]=1; - unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - if (socketList[realIndex]->IsBerkleySocket()) - { - RNS2_SendParameters bsp; - bsp.data = (char*) fakeData; - bsp.length = 2; - bsp.systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); - bsp.systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); - bsp.ttl=ttl; - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*)bsp.data, BYTES_TO_BITS(bsp.length), bsp.systemAddress); - socketList[realIndex]->Send(&bsp, _FILE_AND_LINE_); - } -} - - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Attatches a Plugin interface to run code automatically on message receipt in the Receive call -// -// \param messageHandler Pointer to a plugin to attach -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AttachPlugin( PluginInterface2 *plugin ) -{ - if (IsInsideNetworkThreadReceive()) - return; - - bool isNotThreadsafe = plugin->UsesReliabilityLayer(); - if (isNotThreadsafe) - { - if (pluginListNTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) - { - plugin->SetRakPeerInterface(this); - plugin->OnAttach(); - pluginListNTS.Insert(plugin, _FILE_AND_LINE_); - } - } - else - { - if (pluginListTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) - { - plugin->SetRakPeerInterface(this); - plugin->OnAttach(); - pluginListTS.Insert(plugin, _FILE_AND_LINE_); - } - } - - pluginListNetworkThreadMutex.Lock(); - if (pluginListNetworkThread.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) - pluginListNetworkThread.Insert(plugin, _FILE_AND_LINE_); - pluginListNetworkThreadMutex.Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Detaches a Plugin interface to run code automatically on message receipt -// -// \param messageHandler Pointer to a plugin to detach -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DetachPlugin( PluginInterface2 *plugin ) -{ - if (plugin==0 || IsInsideNetworkThreadReceive()) - return; - - unsigned int index; - - bool isNotThreadsafe = plugin->UsesReliabilityLayer(); - if (isNotThreadsafe) - { - index = pluginListNTS.GetIndexOf(plugin); - if (index!=MAX_UNSIGNED_LONG) - { - // Unordered list so delete from end for speed - pluginListNTS[index]=pluginListNTS[pluginListNTS.Size()-1]; - pluginListNTS.RemoveFromEnd(); - } - } - else - { - index = pluginListTS.GetIndexOf(plugin); - if (index!=MAX_UNSIGNED_LONG) - { - // Unordered list so delete from end for speed - pluginListTS[index]=pluginListTS[pluginListTS.Size()-1]; - pluginListTS.RemoveFromEnd(); - } - } - // Removed under the lock the update thread dispatches under, so once this returns the plugin - // is not inside OnReceiveNetworkThread and will not be called again. - pluginListNetworkThreadMutex.Lock(); - index = pluginListNetworkThread.GetIndexOf(plugin); - if (index!=MAX_UNSIGNED_LONG) - { - pluginListNetworkThread[index]=pluginListNetworkThread[pluginListNetworkThread.Size()-1]; - pluginListNetworkThread.RemoveFromEnd(); - } - pluginListNetworkThreadMutex.Unlock(); - - plugin->OnDetach(); - plugin->SetRakPeerInterface(0); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Put a packet back at the end of the receive queue in case you don't want to deal with it immediately -// -// packet The packet you want to push back. -// pushAtHead True to push the packet so that the next receive call returns it. False to push it at the end of the queue (obviously pushing it at the end makes the packets out of order) -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::PushBackPacket( Packet *packet, bool pushAtHead) -{ - if (packet==0) - return; - - unsigned i; - for (i=0; i < pluginListTS.Size(); i++) - pluginListTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); - - packetReturnMutex.Lock(); - if (pushAtHead) - packetReturnQueue.PushAtHead(packet,0,_FILE_AND_LINE_); - else - packetReturnQueue.Push(packet,_FILE_AND_LINE_); - packetReturnMutex.Unlock(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress) -{ - BufferedCommandStruct *bcs; - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->data = 0; - bcs->systemIdentifier.systemAddress=systemAddress; - bcs->systemIdentifier.rakNetGuid=guid; - bcs->command=BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS; - bufferedCommands.Push(bcs); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -Packet* RakPeer::AllocatePacket(unsigned dataSize) -{ - return AllocPacket(dataSize, _FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakNetSocket2* RakPeer::GetSocket( const SystemAddress target ) -{ - // Send a query to the thread to get the socket, and return when we got it - BufferedCommandStruct *bcs; - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; - bcs->systemIdentifier=target; - bcs->data=0; - bufferedCommands.Push(bcs); - - // Block up to one second to get the socket, although it should actually take virtually no time - SocketQueryOutput *sqo; - MafiaNet::TimeMS stopWaiting = MafiaNet::GetTimeMS()+1000; - DataStructures::List output; - while (manualUpdate || MafiaNet::GetTimeMS() < stopWaiting) - { - if (manualUpdate) - RunManualUpdate(); // answers the query on this thread - else if (isMainLoopThreadActive==false) - return 0; - else - RakSleep(0); - - sqo = socketQueryOutput.Pop(); - if (sqo) - { - output=sqo->sockets; - sqo->sockets.Clear(false, _FILE_AND_LINE_); - socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); - if (output.Size()) - return output[0]; - break; - } - } - return 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetSockets( DataStructures::List &sockets ) -{ - sockets.Clear(false, _FILE_AND_LINE_); - - // Send a query to the thread to get the socket, and return when we got it - BufferedCommandStruct *bcs; - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; - bcs->systemIdentifier=UNASSIGNED_SYSTEM_ADDRESS; - bcs->data=0; - bufferedCommands.Push(bcs); - - // Block up to one second to get the socket, although it should actually take virtually no time - SocketQueryOutput *sqo; -// RakNetSocket2* output; - for(;;) - { - if (manualUpdate) - RunManualUpdate(); // answers the query on this thread - else if (isMainLoopThreadActive==false) - return; - else - RakSleep(0); - - sqo = socketQueryOutput.Pop(); - if (sqo) - { - sockets=sqo->sockets; - sqo->sockets.Clear(false, _FILE_AND_LINE_); - socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); - return; - } - } - return; -} -void RakPeer::ReleaseSockets( DataStructures::List &sockets ) -{ - sockets.Clear(false,_FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Adds simulated ping and packet loss to the outgoing data flow. -// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and maxSendBPS value on each. -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance) -{ -#ifndef _DEBUG - // unused parameters - (void)packetloss; - (void)minExtraPing; - (void)extraPingVariance; -#endif - -#ifdef _DEBUG - if (remoteSystemList) - { - unsigned short i; - for (i=0; i < maximumNumberOfPeers; i++) - //for (i=0; i < remoteSystemListSize; i++) - remoteSystemList[i].reliabilityLayer.ApplyNetworkSimulator(packetloss, minExtraPing, extraPingVariance); - } - - _packetloss=packetloss; - _minExtraPing=minExtraPing; - _extraPingVariance=extraPingVariance; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void RakPeer::SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond ) -{ - maxOutgoingBPS=maxBitsPerSecond; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Returns if you previously called ApplyNetworkSimulator -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsNetworkSimulatorActive( void ) -{ -#ifdef _DEBUG - return _packetloss>0 || _minExtraPing>0 || _extraPingVariance>0; -#else - return false; -#endif -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::WriteOutOfBandHeader(MafiaNet::BitStream *bitStream) -{ - bitStream->Write((MessageID)ID_OUT_OF_BAND_INTERNAL); - bitStream->Write(myGuid); - bitStream->WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData) -{ - userUpdateThreadPtr=_userUpdateThreadPtr; - userUpdateThreadData=_userUpdateThreadData; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetSocketFactory(RakNetSocket2Factory *factory) -{ - RakAssert(IsActive() == false); - socketFactory = factory; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetManualUpdate(bool manual) -{ - RakAssert(IsActive() == false); - manualUpdate = manual; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RunManualUpdate() -{ - RakAssert(manualUpdate); - // The same steps as one pass of UpdateNetworkLoop. - if (userUpdateThreadPtr) - userUpdateThreadPtr(this, userUpdateThreadData); - BitStream updateBitStream( MAXIMUM_MTU_SIZE -#if LIBCAT_SECURITY==1 - + cat::AuthenticatedEncryption::OVERHEAD_BYTES -#endif - ); - RunUpdateCycle(updateBitStream); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetIncomingDatagramEventHandler( bool (*_incomingDatagramEventHandler)(RNS2RecvStruct *) ) -{ - incomingDatagramEventHandler=_incomingDatagramEventHandler; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex ) -{ - if ( IsActive() == false ) - return false; - - if (host==0 || host[0]==0) - return false; - - // If this assert hits then Startup wasn't called or the call failed. - RakAssert(connectionSocketIndex < socketList.Size()); - - // This is a security measure. Don't send data longer than this value - RakAssert(dataLength <= (MAX_OFFLINE_DATA_LENGTH + sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID))); - - if (host==0) - return false; - - // 34 bytes - MafiaNet::BitStream bitStream; - WriteOutOfBandHeader(&bitStream); - - if (dataLength>0) - { - bitStream.Write(data, dataLength); - } - - unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); - - /* - SystemAddress systemAddress; - systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); - systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); - - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); - - SocketLayer::SendTo( socketList[realIndex], (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - */ - - RNS2_SendParameters bsp; - bsp.data = (char*) bitStream.GetData(); - bsp.length = bitStream.GetNumberOfBytesUsed(); - bsp.systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); - bsp.systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*)bsp.data, BYTES_TO_BITS(bsp.length), bsp.systemAddress); - socketList[realIndex]->Send(&bsp, _FILE_AND_LINE_); - - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakNetStatistics * RakPeer::GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns ) -{ - static RakNetStatistics staticStatistics; - RakNetStatistics *systemStats; - if (rns==0) - systemStats=&staticStatistics; - else - systemStats=rns; - - if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) - { - bool firstWrite=false; - // Return a crude sum - for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].isActive) - { - RakNetStatistics rnsTemp; - remoteSystemList[ i ].reliabilityLayer.GetStatistics(&rnsTemp); - - if (firstWrite==false) - { - memcpy(systemStats, &rnsTemp, sizeof(RakNetStatistics)); - firstWrite=true; - } - else - (*systemStats)+=rnsTemp; - } - } - return systemStats; - } - else - { - RemoteSystemStruct * rss; - rss = GetRemoteSystemFromSystemAddress( systemAddress, false, false ); - if ( rss && endThreads==false ) - { - rss->reliabilityLayer.GetStatistics(systemStats); - return systemStats; - } - } - - return 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GetStatisticsList(DataStructures::List &addresses, DataStructures::List &guids, DataStructures::List &statistics) -{ - addresses.Clear(false, _FILE_AND_LINE_); - guids.Clear(false, _FILE_AND_LINE_); - statistics.Clear(false, _FILE_AND_LINE_); - - if ( remoteSystemList == 0 || endThreads == true ) - return; - - unsigned int i; - for (i=0; i < activeSystemListSize; i++) - { - if ((activeSystemList[i])->isActive && - (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED) - { - addresses.Push((activeSystemList[i])->systemAddress, _FILE_AND_LINE_ ); - guids.Push((activeSystemList[i])->guid, _FILE_AND_LINE_ ); - RakNetStatistics rns; - (activeSystemList[i])->reliabilityLayer.GetStatistics(&rns); - statistics.Push(rns, _FILE_AND_LINE_); - } - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::GetStatistics( const unsigned int index, RakNetStatistics *rns ) -{ - if (index < maximumNumberOfPeers && remoteSystemList[ index ].isActive) - { - remoteSystemList[ index ].reliabilityLayer.GetStatistics(rns); - return true; - } - return false; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetReceiveBufferSize(void) -{ - unsigned int size; - packetReturnMutex.Lock(); - size=packetReturnQueue.Size(); - packetReturnMutex.Unlock(); - return size; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread ) const -{ - unsigned i; - - if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) - return -1; - - if (systemAddress.systemIndex!=(SystemIndex)-1 && systemAddress.systemIndex < maximumNumberOfPeers && remoteSystemList[systemAddress.systemIndex].systemAddress==systemAddress && remoteSystemList[ systemAddress.systemIndex ].isActive) - return systemAddress.systemIndex; - - if (calledFromNetworkThread) - { - return GetRemoteSystemIndex(systemAddress); - } - else - { - // remoteSystemList in user and network thread - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].systemAddress == systemAddress ) - return i; - - // If no active results found, try previously active results. - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].systemAddress == systemAddress ) - return i; - } - - return -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -int RakPeer::GetIndexFromGuid( const RakNetGUID guid ) -{ - unsigned i; - - if ( guid == UNASSIGNED_RAKNET_GUID ) - return -1; - - if (guid.systemIndex!=(SystemIndex)-1 && guid.systemIndex < maximumNumberOfPeers && remoteSystemList[guid.systemIndex].guid==guid && remoteSystemList[ guid.systemIndex ].isActive) - return guid.systemIndex; - - // remoteSystemList in user and network thread - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].guid == guid ) - return i; - - // If no active results found, try previously active results. - for ( i = 0; i < maximumNumberOfPeers; i++ ) - if ( remoteSystemList[ i ].guid == guid ) - return i; - - return -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -#if LIBCAT_SECURITY==1 -bool RakPeer::GenerateConnectionRequestChallenge(RequestedConnectionStruct *rcs,PublicKey *publicKey) -{ - CAT_AUDIT_PRINTF("AUDIT: In GenerateConnectionRequestChallenge()\n"); - - rcs->client_handshake = 0; - rcs->publicKeyMode = PKM_INSECURE_CONNECTION; - - if (!publicKey) return true; - - switch (publicKey->publicKeyMode) - { - default: - case PKM_INSECURE_CONNECTION: - break; - - case PKM_ACCEPT_ANY_PUBLIC_KEY: - CAT_OBJCLR(rcs->remote_public_key); - rcs->client_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); - - rcs->publicKeyMode = PKM_ACCEPT_ANY_PUBLIC_KEY; - break; - - case PKM_USE_TWO_WAY_AUTHENTICATION: - if (publicKey->myPublicKey == 0 || publicKey->myPrivateKey == 0 || - publicKey->remoteServerPublicKey == 0) - { - return false; - } - - rcs->client_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); - memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); - - if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || - !rcs->client_handshake->SetIdentity(publicKey->myPublicKey, publicKey->myPrivateKey) || - !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) - { - CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object with identity for this RequestedConnectionStruct\n"); - MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - rcs->client_handshake=0; - return false; - } - - CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object with identity for this RequestedConnectionStruct -- pre-generated challenge\n"); - - rcs->publicKeyMode = PKM_USE_TWO_WAY_AUTHENTICATION; - break; - - case PKM_USE_KNOWN_PUBLIC_KEY: - if (publicKey->remoteServerPublicKey == 0) - return false; - - rcs->client_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); - memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); - - if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || - !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) - { - CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object for this RequestedConnectionStruct\n"); - MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - rcs->client_handshake=0; - return false; - } - - CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object for this RequestedConnectionStruct -- pre-generated challenge\n"); - - rcs->publicKeyMode = PKM_USE_KNOWN_PUBLIC_KEY; - break; - } - - return true; -} -#endif -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime ) -{ - RakAssert(passwordDataLength <= 256); - RakAssert(remotePort!=0); - SystemAddress systemAddress; - if (!systemAddress.FromStringExplicitPort(host,remotePort,socketList[connectionSocketIndex]->GetBoundAddress().GetIPVersion())) - return CANNOT_RESOLVE_DOMAIN_NAME; - - // Already connected? - if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) - return ALREADY_CONNECTED_TO_ENDPOINT; - - //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); - RequestedConnectionStruct *rcs = MafiaNet::OP_NEW(_FILE_AND_LINE_); - - rcs->systemAddress=systemAddress; - rcs->nextRequestTime= MafiaNet::GetTimeMS(); - rcs->requestsMade=0; - rcs->data=0; - rcs->socket=0; - rcs->extraData=extraData; - rcs->socketIndex=connectionSocketIndex; - rcs->actionToTake=RequestedConnectionStruct::CONNECT; - rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; - rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; - // No password means passwordData is null. memcpy's source is declared nonnull, - // so a null source is undefined behaviour even with a length of 0. - if (passwordDataLength > 0) - memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); - rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; - rcs->timeoutTime=timeoutTime; - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: In SendConnectionRequest()\n"); - if (!GenerateConnectionRequestChallenge(rcs,publicKey)) - return SECURITY_INITIALIZATION_FAILED; -#else - (void) publicKey; -#endif - - // Return false if already pending, else push on queue - unsigned int i=0; - requestedConnectionQueueMutex.Lock(); - for (; i < requestedConnectionQueue.Size(); i++) - { - if (requestedConnectionQueue[i]->systemAddress==systemAddress) - { - requestedConnectionQueueMutex.Unlock(); - // Not necessary - //MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); - return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; - } - } - requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); - requestedConnectionQueueMutex.Unlock(); - - return CONNECTION_ATTEMPT_STARTED; -} -ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime, RakNetSocket2* socket ) -{ - RakAssert(passwordDataLength <= 256); - SystemAddress systemAddress; - systemAddress.FromStringExplicitPort(host,remotePort); - - // Already connected? - if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) - return ALREADY_CONNECTED_TO_ENDPOINT; - - //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); - RequestedConnectionStruct *rcs = MafiaNet::OP_NEW(_FILE_AND_LINE_); - - rcs->systemAddress=systemAddress; - rcs->nextRequestTime= MafiaNet::GetTimeMS(); - rcs->requestsMade=0; - rcs->data=0; - rcs->socket=0; - rcs->extraData=extraData; - rcs->socketIndex=connectionSocketIndex; - rcs->actionToTake=RequestedConnectionStruct::CONNECT; - rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; - rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; - // No password means passwordData is null. memcpy's source is declared nonnull, - // so a null source is undefined behaviour even with a length of 0. - if (passwordDataLength > 0) - memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); - rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; - rcs->timeoutTime=timeoutTime; - rcs->socket=socket; - -#if LIBCAT_SECURITY==1 - if (!GenerateConnectionRequestChallenge(rcs,publicKey)) - return SECURITY_INITIALIZATION_FAILED; -#else - (void) publicKey; -#endif - - // Return false if already pending, else push on queue - unsigned int i=0; - requestedConnectionQueueMutex.Lock(); - for (; i < requestedConnectionQueue.Size(); i++) - { - if (requestedConnectionQueue[i]->systemAddress==systemAddress) - { - requestedConnectionQueueMutex.Unlock(); - // Not necessary - //MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); - return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; - } - } - requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); - requestedConnectionQueueMutex.Unlock(); - - return CONNECTION_ATTEMPT_STARTED; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ValidateRemoteSystemLookup(void) const -{ -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystem( const AddressOrGUID systemIdentifier, bool calledFromNetworkThread, bool onlyActive ) const -{ - if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) - return GetRemoteSystemFromGUID(systemIdentifier.rakNetGuid, onlyActive); - else - return GetRemoteSystemFromSystemAddress(systemIdentifier.systemAddress, calledFromNetworkThread, onlyActive); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread, bool onlyActive ) const -{ - unsigned i; - - if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) - return 0; - - if (calledFromNetworkThread) - { - unsigned int index = GetRemoteSystemIndex(systemAddress); - if (index!=(unsigned int) -1) - { - if (onlyActive==false || remoteSystemList[ index ].isActive==true ) - { - RakAssert(remoteSystemList[index].systemAddress==systemAddress); - return remoteSystemList + index; - } - } - } - else - { - int deadConnectionIndex=-1; - - // Active connections take priority. But if there are no active connections, return the first systemAddress match found - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].systemAddress == systemAddress) - { - if ( remoteSystemList[ i ].isActive ) - return remoteSystemList + i; - else if (deadConnectionIndex==-1) - deadConnectionIndex=i; - } - } - - if (deadConnectionIndex!=-1 && onlyActive==false) - return remoteSystemList + deadConnectionIndex; - } - - return 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromGUID( const RakNetGUID guid, bool onlyActive ) const -{ - if (guid==UNASSIGNED_RAKNET_GUID) - return 0; - - unsigned i; - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if (remoteSystemList[ i ].guid == guid && (onlyActive==false || remoteSystemList[ i ].isActive)) - { - return remoteSystemList + i; - } - } - return 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ParseConnectionRequestPacket( RakPeer::RemoteSystemStruct *remoteSystem, const SystemAddress &systemAddress, const char *data, int byteSize ) -{ - MafiaNet::BitStream bs((unsigned char*) data,byteSize,false); - bs.IgnoreBytes(sizeof(MessageID)); - RakNetGUID guid; - bs.Read(guid); - MafiaNet::Time incomingTimestamp; - bs.Read(incomingTimestamp); - unsigned char doSecurity; - bs.Read(doSecurity); - -#if LIBCAT_SECURITY==1 - unsigned char doClientKey; - if (_using_security) - { - // Ignore message on bad state - if (doSecurity != 1 || !remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()) - return; - - // Validate client proof of key - unsigned char proof[cat::EasyHandshake::PROOF_BYTES]; - bs.ReadAlignedBytes(proof, sizeof(proof)); - if (!remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->ValidateProof(proof, sizeof(proof))) - { - remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - - CAT_OBJCLR(remoteSystem->client_public_key); - - bs.Read(doClientKey); - - // Check if client wants to prove identity - if (doClientKey == 1) - { - // Read identity proof - unsigned char ident[cat::EasyHandshake::IDENTITY_BYTES]; - bs.ReadAlignedBytes(ident, sizeof(ident)); - - // If we are listening to these proofs, - if (_require_client_public_key) - { - // Validate client identity - if (!_server_handshake->VerifyInitiatorIdentity(remoteSystem->answer, ident, remoteSystem->client_public_key)) - { - MafiaNet::BitStream bitStream; - bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect - bitStream.Write((unsigned char)2); // Indicate client identity is invalid - SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); - remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - } - - // Otherwise ignore the client public key - } - else - { - // If no client key was provided but it is required, - if (_require_client_public_key) - { - MafiaNet::BitStream bitStream; - bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect - bitStream.Write((unsigned char)1); // Indicate client identity is missing - SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); - remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - } - } -#endif // LIBCAT_SECURITY - - unsigned char *password = bs.GetData()+BITS_TO_BYTES(bs.GetReadOffset()); - int passwordLength = byteSize - BITS_TO_BYTES(bs.GetReadOffset()); - if ( incomingPasswordLength != passwordLength || - memcmp( password, incomingPassword, incomingPasswordLength ) != 0 ) - { - CAT_AUDIT_PRINTF("AUDIT: Invalid password\n"); - // This one we only send once since we don't care if it arrives. - MafiaNet::BitStream bitStream; - bitStream.Write((MessageID)ID_INVALID_PASSWORD); - bitStream.Write(GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - return; - } - - // OK - remoteSystem->connectMode=RemoteSystemStruct::HANDLING_CONNECTION_REQUEST; - - OnConnectionRequest( remoteSystem, incomingTimestamp ); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::OnConnectionRequest( RakPeer::RemoteSystemStruct *remoteSystem, MafiaNet::Time incomingTimestamp ) -{ - MafiaNet::BitStream bitStream; - bitStream.Write((MessageID)ID_CONNECTION_REQUEST_ACCEPTED); - bitStream.Write(remoteSystem->systemAddress); - SystemIndex systemIndex = (SystemIndex) GetIndexFromSystemAddress( remoteSystem->systemAddress, true ); - RakAssert(systemIndex!=65535); - bitStream.Write(systemIndex); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - bitStream.Write(ipList[i]); - bitStream.Write(incomingTimestamp); - bitStream.Write(MafiaNet::GetTime()); - - SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, remoteSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::NotifyAndFlagForShutdown( const SystemAddress systemAddress, bool performImmediate, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority, const MafiaNet::BitStream *reasonData ) -{ - MafiaNet::BitStream temp( sizeof(unsigned char) ); - temp.Write( (MessageID)ID_DISCONNECTION_NOTIFICATION ); - // Optionally append a caller-supplied reason payload right after the 1-byte ID. The ID occupies exactly 8 bits so - // the payload stays byte-aligned; the remote reads it from packet->data+1 (length packet->length-1). Wire-backward - // compatible: peers that only inspect data[0] ignore the extra bytes. - if (reasonData != nullptr && reasonData->GetNumberOfBytesUsed() > 0) - temp.Write( (const char*)reasonData->GetData(), reasonData->GetNumberOfBytesUsed() ); - if (performImmediate) - { - SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, MafiaNet::Reliability::ReliableOrdered, orderingChannel, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); - RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(systemAddress, true, true); - rss->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; - } - else - { - SendBuffered((const char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, MafiaNet::Reliability::ReliableOrdered, orderingChannel, systemAddress, false, RemoteSystemStruct::DISCONNECT_ASAP, 0); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearDisconnectReason( RemoteSystemStruct *remoteSystem ) -{ - if (remoteSystem==0) - return; - if (remoteSystem->disconnectReasonData!=0) - { - rakFree_Ex(remoteSystem->disconnectReasonData, _FILE_AND_LINE_); - remoteSystem->disconnectReasonData=0; - } - remoteSystem->disconnectReasonLength=0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearSessionConfig( RemoteSystemStruct *remoteSystem ) -{ - if (remoteSystem==0) - return; - if (remoteSystem->remoteSessionConfigData!=0) - { - rakFree_Ex(remoteSystem->remoteSessionConfigData, _FILE_AND_LINE_); - remoteSystem->remoteSessionConfigData=0; - } - remoteSystem->remoteSessionConfigLength=0; - if (remoteSystem->withheldConnectionPacketData!=0) - { - rakFree_Ex(remoteSystem->withheldConnectionPacketData, _FILE_AND_LINE_); - remoteSystem->withheldConnectionPacketData=0; - } - remoteSystem->withheldConnectionPacketLength=0; - remoteSystem->sessionConfigAwaitingLocalDecision=false; - remoteSystem->sessionConfigIsConnectingSide=false; - remoteSystem->connectionReportedToApplication=false; - remoteSystem->sessionConfigStartTime=0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::StoreRemoteSessionConfig( RemoteSystemStruct *remoteSystem, const unsigned char *data, unsigned int length ) -{ - if (remoteSystem==0) - return false; - - // An oversized payload is a protocol violation, not something to truncate: the sender enforces the - // same cap in SetSessionConfig, so anything larger did not come from a well-behaved peer. - if (length > MAXIMUM_SESSION_CONFIG_SIZE) - return false; - - if (remoteSystem->remoteSessionConfigData!=0) - { - rakFree_Ex(remoteSystem->remoteSessionConfigData, _FILE_AND_LINE_); - remoteSystem->remoteSessionConfigData=0; - } - remoteSystem->remoteSessionConfigLength=0; - - if (data==0 || length==0) - return true; - - // One extra byte, always zeroed and never counted in the reported length. The payload is - // attacker-controlled and arbitrary bytes, so an application that reaches for strlen/printf on it - // would otherwise run off the end of the buffer. Length remains the authoritative bound. - remoteSystem->remoteSessionConfigData = (unsigned char*) rakMalloc_Ex(length + 1, _FILE_AND_LINE_); - if (remoteSystem->remoteSessionConfigData==0) - return false; - - memcpy(remoteSystem->remoteSessionConfigData, data, length); - remoteSystem->remoteSessionConfigData[length] = 0; - remoteSystem->remoteSessionConfigLength = length; - return true; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Replay the connection packet that was held back while the session handshake ran. This is the single -// point at which an application learns a connection exists, and by construction it runs only once the -// remote's session payload is already stored on the slot. -void RakPeer::ProduceWithheldConnectionPacket( RemoteSystemStruct *remoteSystem, MessageID messageId ) -{ - if (remoteSystem==0) - return; - - Packet *packet; - if (remoteSystem->withheldConnectionPacketData!=0 && remoteSystem->withheldConnectionPacketLength>0) - { - packet=AllocPacket(remoteSystem->withheldConnectionPacketLength, _FILE_AND_LINE_); - memcpy(packet->data, remoteSystem->withheldConnectionPacketData, remoteSystem->withheldConnectionPacketLength); - packet->bitSize = remoteSystem->withheldConnectionPacketLength * 8; - rakFree_Ex(remoteSystem->withheldConnectionPacketData, _FILE_AND_LINE_); - remoteSystem->withheldConnectionPacketData=0; - remoteSystem->withheldConnectionPacketLength=0; - } - else - { - // The original bytes could not be stashed (allocation failure); the message id alone still tells - // the application a connection exists, which is what every caller keys off. - packet=AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); - packet->data[0]=messageId; - packet->bitSize = sizeof(MessageID) * 8; - } - - packet->systemAddress = remoteSystem->systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex = packet->systemAddress.systemIndex; - remoteSystem->connectionReportedToApplication=true; - AddPacketToProducer(packet); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ProduceSessionAbandoned( RemoteSystemStruct *remoteSystem ) -{ - if (remoteSystem==0 || remoteSystem->weInitiatedTheConnection || remoteSystem->sessionConfigAwaitingLocalDecision==false) - return; - - // Once only: the flag is what made the request the application's to answer. - remoteSystem->sessionConfigAwaitingLocalDecision=false; - - Packet *packet=AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); - packet->data[0]=(unsigned char)ID_SESSION_CONFIG_ABANDONED; - packet->bitSize=sizeof(MessageID)*8; - packet->systemAddress=remoteSystem->systemAddress; - packet->systemAddress.systemIndex=remoteSystem->remoteSystemIndex; - packet->guid=remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Server side of the session handshake: answer the client's request, promote the connection to -// CONNECTED and release the ID_NEW_INCOMING_CONNECTION that was withheld. -void RakPeer::SendSessionConfigResponse( RemoteSystemStruct *remoteSystem, const char *data, unsigned int length ) -{ - if (remoteSystem==0) - return; - - MafiaNet::BitStream bitStream; - bitStream.Write((MessageID)ID_SESSION_CONFIG); - if (data && length > 0) - bitStream.Write(data, length); - SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, remoteSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); - - remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; - remoteSystem->sessionConfigStartTime=0; - ProduceWithheldConnectionPacket(remoteSystem, (MessageID)ID_NEW_INCOMING_CONNECTION); - - PingInternal( remoteSystem->systemAddress, true, MafiaNet::Reliability::Unreliable ); - quitAndDataEvents.SetEvent(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendSessionConfigRejection( RemoteSystemStruct *remoteSystem, const char *reason, unsigned int reasonLength ) -{ - if (remoteSystem==0) - return; - - MafiaNet::BitStream bitStream; - bitStream.Write((MessageID)ID_SESSION_CONFIG_REJECTED); - if (reason && reasonLength > 0) - bitStream.Write(reason, reasonLength); - SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, remoteSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); - - // The refusal has to reach the client before the socket goes away, so drop on no-ack rather than - // silently: the client turns ID_SESSION_CONFIG_REJECTED into ID_CONNECTION_ATTEMPT_FAILED itself. - remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ON_NO_ACK; - remoteSystem->sessionConfigStartTime = 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetNumberOfRemoteInitiatedConnections( void ) const -{ - if ( remoteSystemList == 0 || endThreads == true ) - return 0; - - unsigned int numberOfIncomingConnections; - numberOfIncomingConnections = 0; - unsigned int i; - for (i=0; i < activeSystemListSize; i++) - { - if ((activeSystemList[i])->isActive && - (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED && - (activeSystemList[i])->weInitiatedTheConnection==false - ) - { - numberOfIncomingConnections++; - } - } - return numberOfIncomingConnections; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct * RakPeer::AssignSystemAddressToRemoteSystemList( const SystemAddress systemAddress, RemoteSystemStruct::ConnectMode connectionMode, RakNetSocket2* incomingRakNetSocket, bool *thisIPConnectedRecently, SystemAddress bindingAddress, int incomingMTU, RakNetGUID guid, bool useSecurity ) -{ - RemoteSystemStruct * remoteSystem; - unsigned i,j,assignedIndex; - MafiaNet::TimeMS time = MafiaNet::GetTimeMS(); -#ifdef _DEBUG - RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); -#endif - - if (limitConnectionFrequencyFromTheSameIP) - { - if (IsLoopbackAddress(systemAddress,false)==false) - { - for ( i = 0; i < maximumNumberOfPeers; i++ ) - { - if ( remoteSystemList[ i ].isActive==true && - remoteSystemList[ i ].systemAddress.EqualsExcludingPort(systemAddress) && - time >= remoteSystemList[ i ].connectionTime && - time - remoteSystemList[ i ].connectionTime < 100 - ) - { - // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots - // Ignore connection attempts if this IP address connected within the last 100 milliseconds - *thisIPConnectedRecently=true; - ValidateRemoteSystemLookup(); - return 0; - } - } - } - } - - // Don't use a different port than what we received on - bindingAddress.CopyPort(incomingRakNetSocket->GetBoundAddress()); - - *thisIPConnectedRecently=false; - for ( assignedIndex = 0; assignedIndex < maximumNumberOfPeers; assignedIndex++ ) - { - if ( remoteSystemList[ assignedIndex ].isActive==false ) - { - // printf("--- Address %s has become active\n", systemAddress.ToString()); - - remoteSystem=remoteSystemList+assignedIndex; - ReferenceRemoteSystem(systemAddress, assignedIndex); - // Stale reason payload from a prior occupant of this slot must never leak into a new connection. - ClearDisconnectReason(remoteSystem); - // Same for session state: peer.h documents it as cleared on slot reuse, and only the teardown - // paths do it today. Without this, any future path that deactivates a slot without going - // through CloseConnectionInternal2 would hand the previous occupant payload to the new one. - ClearSessionConfig(remoteSystem); - remoteSystem->MTUSize=defaultMTUSize; - remoteSystem->guid=guid; - remoteSystem->isActive = true; // This one line causes future incoming packets to go through the reliability layer - // Reserve this reliability layer for ourselves. - // - // incomingMTU comes off the wire -- ID_OPEN_CONNECTION_REQUEST_2 on the accepting side, - // ID_OPEN_CONNECTION_REPLY_2 on the connecting one -- so it is whatever the remote peer - // said, and must be clamped rather than trusted. MTUSize sizes every datagram the - // reliability layer builds into buffers declared MAXIMUM_MTU_SIZE bytes long, so a - // larger value writes past the end of them. A peer built against a higher cap is enough - // to produce one; a hostile peer can name any uint16. This used to be a bare RakAssert, - // which is compiled out of exactly the builds that ship. - if (incomingMTU > MAXIMUM_MTU_SIZE) - incomingMTU = MAXIMUM_MTU_SIZE; - if (incomingMTU > remoteSystem->MTUSize) - remoteSystem->MTUSize=incomingMTU; - RakAssert(remoteSystem->MTUSize <= MAXIMUM_MTU_SIZE); - remoteSystem->reliabilityLayer.Reset(true, remoteSystem->MTUSize, useSecurity); - remoteSystem->reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); - remoteSystem->reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); - remoteSystem->reliabilityLayer.SetTimeoutTime(defaultTimeoutTime); - AddToActiveSystemList(assignedIndex); - if (incomingRakNetSocket->GetBoundAddress()==bindingAddress) - { - remoteSystem->rakNetSocket=incomingRakNetSocket; - } - else - { - // Originally this searched ipList for the internal address the datagram came in on and - // forced the reply onto it, so a multi-homed machine answers from the same IP. That was - // disabled upstream (06/26/09, unconfirmed report that the Vista firewall blocks the reply - // if we force a binding), which left the search computing an index nobody read. Always - // reply on the socket the datagram came in on. - remoteSystem->rakNetSocket=incomingRakNetSocket; - } - - for ( j = 0; j < (unsigned) PING_TIMES_ARRAY_SIZE; j++ ) - { - remoteSystem->pingAndClockDifferential[ j ].pingTime = 65535; - remoteSystem->pingAndClockDifferential[ j ].clockDifferential = 0; - } - - remoteSystem->connectMode=connectionMode; - remoteSystem->pingAndClockDifferentialWriteIndex = 0; - remoteSystem->lowestPing = 65535; - remoteSystem->nextPingTime = 0; // Ping immediately - remoteSystem->weInitiatedTheConnection = false; - remoteSystem->connectionTime = time; - remoteSystem->myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; - remoteSystem->lastReliableSend=time; - -#ifdef _DEBUG - int indexLoopupCheck=GetIndexFromSystemAddress( systemAddress, true ); - if ((int) indexLoopupCheck!=(int) assignedIndex) - { - RakAssert((int) indexLoopupCheck==(int) assignedIndex); - } -#endif - - return remoteSystem; - } - } - - return 0; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Adjust the first four bytes (treated as unsigned int) of the pointer -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ShiftIncomingTimestamp( unsigned char *data, const SystemAddress &systemAddress ) const -{ -#ifdef _DEBUG - RakAssert( IsActive() ); - RakAssert( data ); -#endif - - MafiaNet::BitStream timeBS( data, sizeof(MafiaNet::Time), false); - MafiaNet::Time encodedTimestamp; - timeBS.Read(encodedTimestamp); - - encodedTimestamp = encodedTimestamp - GetBestClockDifferential( systemAddress ); - timeBS.SetWriteOffset(0); - timeBS.Write(encodedTimestamp); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm -MafiaNet::Time RakPeer::GetBestClockDifferential( const SystemAddress systemAddress ) const -{ - RemoteSystemStruct *remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - - if ( remoteSystem == 0 ) - return 0; - - return GetClockDifferentialInt(remoteSystem); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::RemoteSystemLookupHashIndex(const SystemAddress &sa) const -{ - return SystemAddress::ToInteger(sa) % ((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ReferenceRemoteSystem(const SystemAddress &sa, unsigned int remoteSystemListIndex) -{ -// #ifdef _DEBUG -// for ( unsigned int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) -// { -// if (remoteSystemList[remoteSystemIndex].isActive ) -// { -// unsigned int debugHashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); -// RakAssert(debugHashIndex==remoteSystemIndex); -// } -// } -// #endif - - - SystemAddress oldAddress = remoteSystemList[remoteSystemListIndex].systemAddress; - if (oldAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - // The system might be active if rerouting -// RakAssert(remoteSystemList[remoteSystemListIndex].isActive==false); - - // Remove the reference if the reference is pointing to this inactive system - if (GetRemoteSystem(oldAddress)==&remoteSystemList[remoteSystemListIndex]) - DereferenceRemoteSystem(oldAddress); - } - DereferenceRemoteSystem(sa); - -// #ifdef _DEBUG -// for ( unsigned int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) -// { -// if (remoteSystemList[remoteSystemIndex].isActive ) -// { -// unsigned int debugHashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); -// if (debugHashIndex!=remoteSystemIndex) -// { -// RakAssert(debugHashIndex==remoteSystemIndex); -// } -// } -// } -// #endif - - - remoteSystemList[remoteSystemListIndex].systemAddress=sa; - - unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); - RemoteSystemIndex *rsi; - rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); - if (remoteSystemLookup[hashIndex]==0) - { - rsi->next=0; - rsi->index=remoteSystemListIndex; - remoteSystemLookup[hashIndex]=rsi; - } - else - { - RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; - while (cur->next!=0) - { - cur=cur->next; - } - - rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); - rsi->next=0; - rsi->index=remoteSystemListIndex; - cur->next=rsi; - } - -// #ifdef _DEBUG -// for ( unsigned int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) -// { -// if (remoteSystemList[remoteSystemIndex].isActive ) -// { -// unsigned int debugHashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); -// RakAssert(debugHashIndex==remoteSystemIndex); -// } -// } -// #endif - - - RakAssert(GetRemoteSystemIndex(sa)==remoteSystemListIndex); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DereferenceRemoteSystem(const SystemAddress &sa) -{ - unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); - RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; - RemoteSystemIndex *last = 0; - while (cur!=0) - { - if (remoteSystemList[cur->index].systemAddress==sa) - { - if (last==0) - { - remoteSystemLookup[hashIndex]=cur->next; - } - else - { - last->next=cur->next; - } - remoteSystemIndexPool.Release(cur,_FILE_AND_LINE_); - break; - } - last=cur; - cur=cur->next; - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetRemoteSystemIndex(const SystemAddress &sa) const -{ - unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); - RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; - while (cur!=0) - { - if (remoteSystemList[cur->index].systemAddress==sa) - return cur->index; - cur=cur->next; - } - return (unsigned int) -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RakPeer::RemoteSystemStruct* RakPeer::GetRemoteSystem(const SystemAddress &sa) const -{ - unsigned int remoteSystemIndex = GetRemoteSystemIndex(sa); - if (remoteSystemIndex==(unsigned int)-1) - return 0; - return remoteSystemList + remoteSystemIndex; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearRemoteSystemLookup(void) -{ - remoteSystemIndexPool.Clear(_FILE_AND_LINE_); - MafiaNet::OP_DELETE_ARRAY(remoteSystemLookup,_FILE_AND_LINE_); - remoteSystemLookup=0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::AddToActiveSystemList(unsigned int remoteSystemListIndex) -{ - activeSystemList[activeSystemListSize++]=remoteSystemList+remoteSystemListIndex; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::RemoveFromActiveSystemList(const SystemAddress &sa) -{ - unsigned int i; - for (i=0; i < activeSystemListSize; i++) - { - RemoteSystemStruct *rss=activeSystemList[i]; - if (rss->systemAddress==sa) - { - activeSystemList[i]=activeSystemList[activeSystemListSize-1]; - activeSystemListSize--; - return; - } - } - RakAssert("activeSystemList invalid, entry not found in RemoveFromActiveSystemList. Ensure that AddToActiveSystemList and RemoveFromActiveSystemList are called by the same thread." && 0); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -/* -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::LookupIndexUsingHashIndex(const SystemAddress &sa) const -{ - unsigned int scanCount=0; - unsigned int index = RemoteSystemLookupHashIndex(sa); - if (remoteSystemLookup[index].index==(unsigned int)-1) - return (unsigned int) -1; - while (remoteSystemList[remoteSystemLookup[index].index].systemAddress!=sa) - { - if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) - index=0; - if (++scanCount>(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) - return (unsigned int) -1; - if (remoteSystemLookup[index].index==-1) - return (unsigned int) -1; - } - return index; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::RemoteSystemListIndexUsingHashIndex(const SystemAddress &sa) const -{ - unsigned int index = LookupIndexUsingHashIndex(sa); - if (index!=(unsigned int) -1) - { - return remoteSystemLookup[index].index; - } - return (unsigned int) -1; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::FirstFreeRemoteSystemLookupIndex(const SystemAddress &sa) const -{ -// unsigned int collisionCount=0; - unsigned int index = RemoteSystemLookupHashIndex(sa); - while (remoteSystemLookup[index].index!=(unsigned int)-1) - { - if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) - index=0; -// collisionCount++; - } -// printf("%i collisions. Using index %i\n", collisionCount, index); - return index; -} -*/ -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::IsLoopbackAddress(const AddressOrGUID &systemIdentifier, bool matchPort) const -{ - if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) - return systemIdentifier.rakNetGuid==myGuid; - - for (int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS && ipList[i]!=UNASSIGNED_SYSTEM_ADDRESS; i++) - { - if (matchPort) - { - if (ipList[i]==systemIdentifier.systemAddress) - return true; - } - else - { - if (ipList[i].EqualsExcludingPort(systemIdentifier.systemAddress)) - return true; - } - } - - return (matchPort==true && systemIdentifier.systemAddress==firstExternalID) || - (matchPort==false && systemIdentifier.systemAddress.EqualsExcludingPort(firstExternalID)); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SystemAddress RakPeer::GetLoopbackAddress(void) const -{ - - return ipList[0]; - - - -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::AllowIncomingConnections(void) const -{ - // Counts peers still running the session handshake as well as fully established ones. - // GetNumberOfRemoteInitiatedConnections() deliberately reports only CONNECTED peers -- that is what - // an application means by "players" -- but admission control must not share that view: a peer parked - // in EXCHANGING_SESSION_DATA already owns a slot, and counting only CONNECTED would let peers that - // stall the handshake push the real total past SetMaximumIncomingConnections(). - if ( remoteSystemList == 0 || endThreads == true ) - return false; - - return AllowIncomingConnection(UNASSIGNED_SYSTEM_ADDRESS); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::AllowIncomingConnection(const SystemAddress &address) const -{ - if ( remoteSystemList == 0 || endThreads == true ) - return false; - - SessionAdmission::Limits limits; - limits.maxIncoming = GetMaximumIncomingConnections(); - limits.maxPending = maximumPendingSessions; - limits.maxPendingPerAddress = maximumPendingSessionsPerAddress; - - SessionAdmission::Counts counts; - CountRemoteInitiatedPeers(address, counts.connected, counts.exchanging, counts.pending, counts.pendingFromAddress); - return SessionAdmission::MayStartConnecting(limits, counts); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// A pending peer is one that asked to connect and is not CONNECTED yet: still proving the transport -// (UNVERIFIED_SENDER, HANDLING_CONNECTION_REQUEST) or in the session handshake. A peer being torn down is -// in none of these, so a refusal frees its share of the pool at once rather than after the ack. -void RakPeer::CountRemoteInitiatedPeers(const SystemAddress &address, unsigned int &connected, unsigned int &exchanging, unsigned int &pending, unsigned int &pendingFromAddress) const -{ - connected = exchanging = pending = pendingFromAddress = 0; - for (unsigned int i=0; i < activeSystemListSize; i++) - { - const RemoteSystemStruct *system = activeSystemList[i]; - if (!system->isActive || system->weInitiatedTheConnection) - continue; - - switch (system->connectMode) - { - case RemoteSystemStruct::CONNECTED: - connected++; - break; - case RemoteSystemStruct::EXCHANGING_SESSION_DATA: - exchanging++; - // A peer in the handshake is pending too. - [[fallthrough]]; - case RemoteSystemStruct::UNVERIFIED_SENDER: - case RemoteSystemStruct::HANDLING_CONNECTION_REQUEST: - pending++; - if (address!=UNASSIGNED_SYSTEM_ADDRESS && system->systemAddress.EqualsExcludingPort(address)) - pendingFromAddress++; - break; - default: - break; - } - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -MafiaNet::TimeMS RakPeer::GetSessionTimeoutFor(RemoteSystemStruct *remoteSystem) const -{ - return sessionTimeout != 0 ? sessionTimeout : remoteSystem->reliabilityLayer.GetTimeoutTime(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DeallocRNS2RecvStruct(RNS2RecvStruct *s, const char *file, unsigned int line) -{ - bufferedPacketsFreePoolMutex.Lock(); - bufferedPacketsFreePool.Push(s, file, line); - bufferedPacketsFreePoolMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RNS2RecvStruct *RakPeer::AllocRNS2RecvStruct(const char *file, unsigned int line) -{ - bufferedPacketsFreePoolMutex.Lock(); - if (bufferedPacketsFreePool.Size()>0) - { - RNS2RecvStruct *s = bufferedPacketsFreePool.Pop(); - bufferedPacketsFreePoolMutex.Unlock(); - return s; - } - else - { - bufferedPacketsFreePoolMutex.Unlock(); - return MafiaNet::OP_NEW(file,line); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearBufferedPackets(void) -{ - bufferedPacketsFreePoolMutex.Lock(); - while (bufferedPacketsFreePool.Size()>0) - MafiaNet::OP_DELETE(bufferedPacketsFreePool.Pop(), _FILE_AND_LINE_); - bufferedPacketsFreePoolMutex.Unlock(); - - bufferedPacketsQueueMutex.Lock(); - while (bufferedPacketsQueue.Size()>0) - MafiaNet::OP_DELETE(bufferedPacketsQueue.Pop(), _FILE_AND_LINE_); - bufferedPacketsQueueMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SetupBufferedPackets(void) -{ -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::PushBufferedPacket(RNS2RecvStruct * p) -{ - bufferedPacketsQueueMutex.Lock(); - bufferedPacketsQueue.Push(p, _FILE_AND_LINE_); - bufferedPacketsQueueMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -RNS2RecvStruct *RakPeer::PopBufferedPacket(void) -{ - bufferedPacketsQueueMutex.Lock(); - if (bufferedPacketsQueue.Size()>0) - { - RNS2RecvStruct *s = bufferedPacketsQueue.Pop(); - bufferedPacketsQueueMutex.Unlock(); - return s; - } - bufferedPacketsQueueMutex.Unlock(); - return 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::PingInternal( const SystemAddress target, bool performImmediate, MafiaNet::Reliability reliability ) -{ - if ( IsActive() == false ) - return ; - - MafiaNet::BitStream bitStream(sizeof(unsigned char)+sizeof(MafiaNet::Time)); - bitStream.Write((MessageID)ID_CONNECTED_PING); - bitStream.Write(MafiaNet::GetTime()); - if (performImmediate) - SendImmediate( (char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, reliability, 0, target, false, false, MafiaNet::GetTimeUS(), 0 ); - else - Send( &bitStream, MafiaNet::Priority::Immediate, reliability, 0, target, false ); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::CloseConnectionInternal( const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority ) -{ - RakAssert(orderingChannel < 32); - - if (systemIdentifier.IsUndefined()) - return; - - if (remoteSystemList == 0 || endThreads == true) - return; - - // #med - this is flawed and needs to be resolved properly - socketList[0] is not correct here - CloseConnectionInternal2(systemIdentifier, sendDisconnectionNotification, performImmediate, orderingChannel, disconnectionNotificationPriority, *(socketList[0])); -} - -// #med - better integrate directly in CloseConnectionInternal2() -void RakPeer::CloseConnectionInternal2(const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority, RakNetSocket2& socket, const MafiaNet::BitStream *reasonData) -{ - RakAssert(orderingChannel < 32); - - if (systemIdentifier.IsUndefined()) - return; - - if ( remoteSystemList == 0 || endThreads == true ) - return; - - SystemAddress target; - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - { - target=systemIdentifier.systemAddress; - } - else - { - target=GetSystemAddressFromGuid(systemIdentifier.rakNetGuid); - } - - if (target != UNASSIGNED_SYSTEM_ADDRESS && performImmediate) { - target.FixForIPVersion(socket.GetBoundAddress()); - } - - if (sendDisconnectionNotification) - { - NotifyAndFlagForShutdown(target, performImmediate, orderingChannel, disconnectionNotificationPriority, reasonData); - } - else - { - if (performImmediate) - { - unsigned int index = GetRemoteSystemIndex(target); - if (index!=(unsigned int) -1) - { - if ( remoteSystemList[index].isActive ) - { - RemoveFromActiveSystemList(target); - - // Found the index to stop - // printf("--- Address %s has become inactive\n", remoteSystemList[index].systemAddress.ToString()); - remoteSystemList[index].isActive = false; - - remoteSystemList[index].guid=UNASSIGNED_RAKNET_GUID; - - // Reserve this reliability layer for ourselves - //remoteSystemList[ remoteSystemLookup[index].index ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; - - // Clear any remaining messages - RakAssert(remoteSystemList[index].MTUSize <= MAXIMUM_MTU_SIZE); - remoteSystemList[index].reliabilityLayer.Reset(false, remoteSystemList[index].MTUSize, false); - - // Free any stashed disconnect-reason payload (e.g. delivered already, or never consumed) - ClearDisconnectReason(&remoteSystemList[index]); - - // Free any session-handshake state left over from an exchange that never completed - ClearSessionConfig(&remoteSystemList[index]); - - // Not using this socket - remoteSystemList[index].rakNetSocket = 0; - } - } - } - else - { - BufferedCommandStruct *bcs; - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->command=BufferedCommandStruct::BCS_CLOSE_CONNECTION; - bcs->systemIdentifier=target; - // #med - review whether this is safe here - can't the socket be closed/released before its used? - bcs->socket = &socket; - bcs->data=0; - bcs->orderingChannel=orderingChannel; - bcs->priority=disconnectionNotificationPriority; - bufferedCommands.Push(bcs); - } - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendBuffered( const char *data, BitSize_t numberOfBitsToSend, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) -{ - BufferedCommandStruct *bcs; - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->data = (char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES(numberOfBitsToSend), _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy - if (bcs->data==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); - return; - } - - RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); - RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); - RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); - - memcpy(bcs->data, data, (size_t) BITS_TO_BYTES(numberOfBitsToSend)); - bcs->numberOfBitsToSend=numberOfBitsToSend; - bcs->priority=priority; - bcs->reliability=reliability; - bcs->orderingChannel=orderingChannel; - bcs->systemIdentifier=systemIdentifier; - bcs->broadcast=broadcast; - bcs->connectionMode=connectionMode; - bcs->receipt=receipt; - bcs->command=BufferedCommandStruct::BCS_SEND; - bufferedCommands.Push(bcs); - - if (priority==MafiaNet::Priority::Immediate) - { - // Forces pending sends to go out now, rather than waiting to the next update interval - quitAndDataEvents.SetEvent(); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::SendBufferedList( const char **data, const int *lengths, const int numParameters, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) -{ - BufferedCommandStruct *bcs; - unsigned int totalLength=0; - unsigned int lengthOffset; - int i; - for (i=0; i < numParameters; i++) - { - if (lengths[i]>0) - totalLength+=lengths[i]; - } - if (totalLength==0) - return; - - char *dataAggregate; - dataAggregate = (char*) rakMalloc_Ex( (size_t) totalLength, _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy - if (dataAggregate==0) - { - notifyOutOfMemory(_FILE_AND_LINE_); - return; - } - for (i=0, lengthOffset=0; i < numParameters; i++) - { - if (lengths[i]>0) - { - memcpy(dataAggregate+lengthOffset, data[i], lengths[i]); - lengthOffset+=lengths[i]; - } - } - - if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) - { - SendLoopback(dataAggregate,totalLength); - rakFree_Ex(dataAggregate,_FILE_AND_LINE_); - return; - } - - RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); - RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); - RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); - - bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); - bcs->data = dataAggregate; - bcs->numberOfBitsToSend=BYTES_TO_BITS(totalLength); - bcs->priority=priority; - bcs->reliability=reliability; - bcs->orderingChannel=orderingChannel; - bcs->systemIdentifier=systemIdentifier; - bcs->broadcast=broadcast; - bcs->connectionMode=connectionMode; - bcs->receipt=receipt; - bcs->command=BufferedCommandStruct::BCS_SEND; - bufferedCommands.Push(bcs); - - if (priority==MafiaNet::Priority::Immediate) - { - // Forces pending sends to go out now, rather than waiting to the next update interval - quitAndDataEvents.SetEvent(); - } -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::SendImmediate( char *data, BitSize_t numberOfBitsToSend, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool useCallerDataAllocation, MafiaNet::TimeUS currentTime, uint32_t receipt ) -{ - unsigned *sendList; - unsigned sendListSize; - bool callerDataAllocationUsed; - unsigned int remoteSystemIndex, sendListIndex; // Iterates into the list of remote systems -// unsigned numberOfBytesUsed = (unsigned) BITS_TO_BYTES(numberOfBitsToSend); - callerDataAllocationUsed=false; - - sendListSize=0; - - if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) - remoteSystemIndex=GetIndexFromSystemAddress( systemIdentifier.systemAddress, true ); - else if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) - remoteSystemIndex=GetSystemIndexFromGuid(systemIdentifier.rakNetGuid); - else - remoteSystemIndex=(unsigned int) -1; - - // 03/06/06 - If broadcast is false, use the optimized version of GetIndexFromSystemAddress - if (broadcast==false) - { - if (remoteSystemIndex==(unsigned int) -1) - { -#ifdef _DEBUG -// int debugIndex = GetRemoteSystemIndex(systemIdentifier.systemAddress); -#endif - return false; - } - - #if USE_ALLOCA==1 - sendList=(unsigned *)alloca(sizeof(unsigned)); - #else - sendList = (unsigned *) rakMalloc_Ex(sizeof(unsigned), _FILE_AND_LINE_); - #endif - - if (remoteSystemList[remoteSystemIndex].isActive && - remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP && - remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY && - remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ON_NO_ACK) - { - sendList[0]=remoteSystemIndex; - sendListSize=1; - } - } - else - { - #if USE_ALLOCA==1 - sendList=(unsigned *)alloca(sizeof(unsigned)*maximumNumberOfPeers); - #else - sendList = (unsigned *) rakMalloc_Ex(sizeof(unsigned)*maximumNumberOfPeers, _FILE_AND_LINE_); - #endif - - // remoteSystemList in network thread - unsigned int idx; - for ( idx = 0; idx < maximumNumberOfPeers; idx++ ) - { - if (remoteSystemIndex!=(unsigned int) -1 && idx==remoteSystemIndex) - continue; - - // Peers still running the session handshake are excluded: the application has not been told they - // exist, so broadcasting to them would hand application data to a peer it never accepted -- and - // possibly to one it is about to refuse with RejectSession(). Internal handshake traffic is always - // directed, so it never reaches this branch. - if ( remoteSystemList[ idx ].isActive && remoteSystemList[ idx ].systemAddress != UNASSIGNED_SYSTEM_ADDRESS && - remoteSystemList[ idx ].connectMode != RemoteSystemStruct::EXCHANGING_SESSION_DATA ) - sendList[sendListSize++]=idx; - } - } - - if (sendListSize==0) - { - #if !defined(USE_ALLOCA) - rakFree_Ex(sendList, _FILE_AND_LINE_ ); - #endif - - return false; - } - - for (sendListIndex=0; sendListIndex < sendListSize; sendListIndex++) - { - // Send may split the packet and thus deallocate data. Don't assume data is valid if we use the callerAllocationData - bool useData = useCallerDataAllocation && callerDataAllocationUsed==false && sendListIndex+1==sendListSize; - remoteSystemList[sendList[sendListIndex]].reliabilityLayer.Send( data, numberOfBitsToSend, priority, reliability, orderingChannel, useData==false, remoteSystemList[sendList[sendListIndex]].MTUSize, currentTime, receipt ); - if (useData) - callerDataAllocationUsed=true; - - if (reliability==MafiaNet::Reliability::Reliable || - reliability==MafiaNet::Reliability::ReliableOrdered || - reliability==MafiaNet::Reliability::ReliableSequenced || - reliability==MafiaNet::Reliability::ReliableWithAckReceipt || - reliability==MafiaNet::Reliability::ReliableOrderedWithAckReceipt -// || -// reliability==RELIABLE_SEQUENCED_WITH_ACK_RECEIPT - ) - remoteSystemList[sendList[sendListIndex]].lastReliableSend=(MafiaNet::TimeMS)(currentTime/(MafiaNet::TimeUS)1000); - } - -#if !defined(USE_ALLOCA) - rakFree_Ex(sendList, _FILE_AND_LINE_ ); -#endif - - // Return value only meaningful if true was passed for useCallerDataAllocation. Means the reliability layer used that data copy, so the caller should not deallocate it - return callerDataAllocationUsed; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ResetSendReceipt(void) -{ - sendReceiptSerialMutex.Lock(); - sendReceiptSerial=1; - sendReceiptSerialMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::OnConnectedPong(MafiaNet::Time sendPingTime, MafiaNet::Time sendPongTime, RemoteSystemStruct *remoteSystem) -{ - MafiaNet::Time ping; -// MafiaNet::TimeMS lastPing; - MafiaNet::Time time = MafiaNet::GetTime(); // Update the time value to be accurate - if (time > sendPingTime) - ping = time - sendPingTime; - else - ping=0; - -// lastPing = remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime; - - remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime = ( unsigned short ) ping; - // Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm - // Divide each integer by 2, rather than the sum by 2, to prevent overflow - remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].clockDifferential = sendPongTime - ( time/2 + sendPingTime/2 ); - - if ( remoteSystem->lowestPing == (unsigned short)-1 || remoteSystem->lowestPing > (int) ping ) - remoteSystem->lowestPing = (unsigned short) ping; - - if ( ++( remoteSystem->pingAndClockDifferentialWriteIndex ) == (MafiaNet::Time) PING_TIMES_ARRAY_SIZE ) - remoteSystem->pingAndClockDifferentialWriteIndex = 0; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearBufferedCommands(void) -{ - BufferedCommandStruct *bcs; - - while ((bcs=bufferedCommands.Pop())!=0) - { - if (bcs->data) - rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); - - bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); - } - bufferedCommands.Clear(_FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearSocketQueryOutput(void) -{ - socketQueryOutput.Clear(_FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::ClearRequestedConnectionList(void) -{ - DataStructures::Queue freeQueue; - requestedConnectionQueueMutex.Lock(); - while (requestedConnectionQueue.Size()) - freeQueue.Push(requestedConnectionQueue.Pop(), _FILE_AND_LINE_ ); - requestedConnectionQueueMutex.Unlock(); - unsigned i; - for (i=0; i < freeQueue.Size(); i++) - { -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: In ClearRequestedConnectionList(), Deleting freeQueue index %i client_handshake %x\n", i, freeQueue[i]->client_handshake); - MafiaNet::OP_DELETE(freeQueue[i]->client_handshake,_FILE_AND_LINE_); -#endif - MafiaNet::OP_DELETE(freeQueue[i], _FILE_AND_LINE_ ); - } -} -bool RakPeer::IsInsideNetworkThreadReceive(void) const -{ - return networkThreadDispatcher.load()==std::this_thread::get_id(); -} - -void RakPeer::SetNetworkThreadReceiveStopped(bool stopped) -{ - pluginListNetworkThreadMutex.Lock(); - networkThreadReceiveStopped = stopped; - pluginListNetworkThreadMutex.Unlock(); -} - -bool RakPeer::DispatchNetworkThreadReceive(Packet *packet) -{ - pluginListNetworkThreadMutex.Lock(); - if (networkThreadReceiveStopped) - { - pluginListNetworkThreadMutex.Unlock(); - return false; - } - - networkThreadDispatcher = std::this_thread::get_id(); - PluginReceiveResult result = RR_CONTINUE_PROCESSING; - for (unsigned int i=0; i < pluginListNetworkThread.Size() && result==RR_CONTINUE_PROCESSING; i++) - result = pluginListNetworkThread[i]->OnReceiveNetworkThread(packet); - networkThreadDispatcher = std::thread::id(); - pluginListNetworkThreadMutex.Unlock(); - - if (result==RR_STOP_PROCESSING_AND_DEALLOCATE) - DeallocatePacket(packet); - return result!=RR_CONTINUE_PROCESSING; -} - -inline void RakPeer::AddPacketToProducer(MafiaNet::Packet *p) -{ - packetReturnMutex.Lock(); - packetReturnQueue.Push(p,_FILE_AND_LINE_); - packetReturnMutex.Unlock(); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -union Buff6AndBuff8 -{ - unsigned char buff6[6]; - uint64_t buff8; -}; -uint64_t RakPeerInterface::Get64BitUniqueRandomNumber(void) -{ - // Mac address is a poor solution because you can't have multiple connections from the same system - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#if defined(_WIN32) - uint64_t g= MafiaNet::GetTimeUS(); - - MafiaNet::TimeUS lastTime, thisTime; - int j; - // Sleep a small random time, then use the last 4 bits as a source of randomness - for (j=0; j < 8; j++) - { - lastTime = MafiaNet::GetTimeUS(); - RakSleep(1); - RakSleep(0); - thisTime = MafiaNet::GetTimeUS(); - MafiaNet::TimeUS diff = thisTime-lastTime; - unsigned int diff4Bits = (unsigned int) (diff & 15); - diff4Bits <<= 32-4; - diff4Bits >>= j*4; - ((char*)&g)[j] ^= diff4Bits; - } - // Timer jitter alone collides when peers are created back to back, and is - // constant under a test's virtual clock. Mix in the same entropy as below. - static std::atomic sequence {0}; - std::random_device entropy; - g ^= ((uint64_t)entropy() << 32) ^ (uint64_t)entropy(); - g ^= (sequence.fetch_add(1, std::memory_order_relaxed) + 1) * 0x9E3779B97F4A7C15ull; - if (g == 0 || g == (uint64_t)-1) - g = 1; - return g; - -#else - // The time alone is not unique: two peers constructed within the same microsecond -- a server - // and a client created back to back in one process -- got the same GUID, and a peer then - // resolved the other's GUID to itself, so CloseConnection() and friends silently did nothing. - // Mix in the OS entropy source and a per-process counter. - struct timeval tv; - gettimeofday(&tv, nullptr); - uint64_t g = (uint64_t)tv.tv_usec + (uint64_t)tv.tv_sec * 1000000; - - static std::atomic sequence {0}; - std::random_device entropy; - g ^= ((uint64_t)entropy() << 32) ^ (uint64_t)entropy(); - g ^= (sequence.fetch_add(1, std::memory_order_relaxed) + 1) * 0x9E3779B97F4A7C15ull; - - // Neither value is a usable GUID: 0 means "not generated" and all-ones is UNASSIGNED_RAKNET_GUID. - if (g == 0 || g == (uint64_t)-1) - g = 1; - return g; -#endif -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::GenerateGUID(void) -{ - myGuid.g=Get64BitUniqueRandomNumber(); - -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// void MafiaNet::ProcessPortUnreachable( SystemAddress systemAddress, RakPeer *rakPeer ) -// { -// (void) binaryAddress; -// (void) port; -// (void) rakPeer; -// -// } -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -namespace MafiaNet { -bool ProcessOfflineNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSocket2* rakNetSocket, bool *isOfflineMessage, MafiaNet::TimeUS timeRead ) -{ - (void) timeRead; - RakPeer::RemoteSystemStruct *remoteSystem; - MafiaNet::Packet *packet; - unsigned i; - - - char str1[64]; - systemAddress.ToString(false, str1,static_cast(64)); - if (rakPeer->IsBanned( str1 )) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - MafiaNet::BitStream bs; - bs.Write((MessageID)ID_CONNECTION_BANNED); - bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - - - RNS2_SendParameters bsp; - bsp.data = (char*) bs.GetData(); - bsp.length = bs.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*) bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - -/* - unsigned i; - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*) bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); - SocketLayer::SendTo( rakNetSocket, (char*) bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - */ - - return true; - } - - - - // The reason for all this is that the reliability layer has no way to tell between offline messages that arrived late for a player that is now connected, - // and a regular encoding. So I insert OFFLINE_MESSAGE_DATA_ID into the stream, the encoding of which is essentially impossible to hit by chance - if (length <=2) - { - *isOfflineMessage=true; - } - else if ( - ((unsigned char)data[0] == ID_UNCONNECTED_PING || - (unsigned char)data[0] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS) && - (size_t) length >= sizeof(unsigned char) + sizeof(MafiaNet::Time) + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(MafiaNet::Time), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if ((unsigned char)data[0] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char) + sizeof(MafiaNet::TimeMS) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(MafiaNet::Time) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if ( - (unsigned char)data[0] == ID_OUT_OF_BAND_INTERNAL && - (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(MessageID) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if ( - ( - (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_1 || - (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_2 || - (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_1 || - (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_2 || - (unsigned char)data[0] == ID_CONNECTION_ATTEMPT_FAILED || - (unsigned char)data[0] == ID_NO_FREE_INCOMING_CONNECTIONS || - (unsigned char)data[0] == ID_CONNECTION_BANNED || - (unsigned char)data[0] == ID_ALREADY_CONNECTED || - (unsigned char)data[0] == ID_IP_RECENTLY_CONNECTED) && - (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) - { - *isOfflineMessage=memcmp(data+sizeof(MessageID), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else if (((unsigned char)data[0] == ID_INCOMPATIBLE_PROTOCOL_VERSION&& - (size_t) length == sizeof(MessageID)*2 + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID))) - { - *isOfflineMessage=memcmp(data+sizeof(MessageID)*2, OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; - } - else - { - *isOfflineMessage=false; - } - - if (*isOfflineMessage) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - // These are all messages from unconnected systems. Messages here can be any size, but are never processed from connected systems. - if ( ( (unsigned char) data[ 0 ] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS - || (unsigned char)(data)[0] == ID_UNCONNECTED_PING) && (size_t) length >= sizeof(unsigned char)+sizeof(MafiaNet::Time)+sizeof(OFFLINE_MESSAGE_DATA_ID) ) - { - if ( (unsigned char)(data)[0] == ID_UNCONNECTED_PING || - rakPeer->AllowIncomingConnections() ) // Open connections with players - { - MafiaNet::BitStream inBitStream( (unsigned char *) data, length, false ); - inBitStream.IgnoreBits(8); - MafiaNet::Time sendPingTime; - inBitStream.Read(sendPingTime); - inBitStream.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - RakNetGUID remoteGuid=UNASSIGNED_RAKNET_GUID; - inBitStream.Read(remoteGuid); - - MafiaNet::BitStream outBitStream; - outBitStream.Write((MessageID)ID_UNCONNECTED_PONG); // Should be named ID_UNCONNECTED_PONG eventually - outBitStream.Write(sendPingTime); - outBitStream.Write(rakPeer->myGuid); - outBitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - - rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Lock(); - // They are connected, so append offline ping data - outBitStream.Write( (char*)rakPeer->offlinePingResponse.GetData(), rakPeer->offlinePingResponse.GetNumberOfBytesUsed() ); - rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Unlock(); - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*)outBitStream.GetData(), outBitStream.GetNumberOfBytesUsed(), systemAddress); - - RNS2_SendParameters bsp; - bsp.data = (char*) outBitStream.GetData(); - bsp.length = outBitStream.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - - // SocketLayer::SendTo( rakNetSocket, (const char*)outBitStream.GetData(), (unsigned int) outBitStream.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - - packet=rakPeer->AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); - packet->data[0]=data[0]; - packet->systemAddress = systemAddress; - packet->guid=remoteGuid; - packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - rakPeer->AddPacketToProducer(packet); - } - } - // UNCONNECTED MESSAGE Pong with no data. - else if ((unsigned char) data[ 0 ] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID) && (size_t) length < sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID)+MAX_OFFLINE_DATA_LENGTH) - { - packet=rakPeer->AllocPacket((unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(MafiaNet::Time)+sizeof(MafiaNet::TimeMS)), _FILE_AND_LINE_); - MafiaNet::BitStream bsIn((unsigned char*) data, length, false); - bsIn.IgnoreBytes(sizeof(unsigned char)); - MafiaNet::Time ping; - bsIn.Read(ping); - bsIn.Read(packet->guid); - - MafiaNet::BitStream bsOut((unsigned char*) packet->data, packet->length, false); - bsOut.ResetWritePointer(); - bsOut.Write((unsigned char)ID_UNCONNECTED_PONG); - MafiaNet::TimeMS pingMS=(MafiaNet::TimeMS)ping; - bsOut.Write(pingMS); - bsOut.WriteAlignedBytes( - (const unsigned char*)data+sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID), - length-sizeof(unsigned char)-sizeof(MafiaNet::Time)-RakNetGUID::size()-sizeof(OFFLINE_MESSAGE_DATA_ID) - ); - - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - rakPeer->AddPacketToProducer(packet); - } - else if ((unsigned char) data[ 0 ] == ID_OUT_OF_BAND_INTERNAL && - (size_t) length > sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)+RakNetGUID::size() && - (size_t) length < MAX_OFFLINE_DATA_LENGTH+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)+RakNetGUID::size()) - { - unsigned int dataLength = (unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(MessageID)); - RakAssert(dataLength<1024); - packet=rakPeer->AllocPacket(dataLength+1, _FILE_AND_LINE_); - RakAssert(packet->length<1024); - - MafiaNet::BitStream bs2((unsigned char*) data, length, false); - bs2.IgnoreBytes(sizeof(MessageID)); - bs2.Read(packet->guid); - - if (data[sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size()]==ID_ADVERTISE_SYSTEM) - { - packet->length--; - packet->bitSize=BYTES_TO_BITS(packet->length); - packet->data[0]=ID_ADVERTISE_SYSTEM; - memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)*2 + RakNetGUID::size(), dataLength-1); - } - else - { - packet->data[0]=ID_OUT_OF_BAND_INTERNAL; - memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size(), dataLength); - } - - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); - packet->guid.systemIndex=packet->systemAddress.systemIndex; - rakPeer->AddPacketToProducer(packet); - } - else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_1) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - MafiaNet::BitStream bsIn((unsigned char*) data,length,false); - bsIn.IgnoreBytes(sizeof(MessageID)); - bsIn.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - RakNetGUID serverGuid; - bsIn.Read(serverGuid); - unsigned char serverHasSecurity; - uint32_t cookie; - (void) cookie; - bsIn.Read(serverHasSecurity); - // Even if the server has security, it may not be required of us if we are in the security exception list - if (serverHasSecurity) - { - bsIn.Read(cookie); - } - - MafiaNet::BitStream bsOut; - bsOut.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_2); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - if (serverHasSecurity) - bsOut.Write(cookie); - - rakPeer->requestedConnectionQueueMutex.Lock(); - for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) - { - RakPeer::RequestedConnectionStruct *rcs; - rcs=rakPeer->requestedConnectionQueue[i]; - if (rcs->systemAddress==systemAddress) - { - if (serverHasSecurity) - { -#if LIBCAT_SECURITY==1 - unsigned char public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; - bsIn.ReadAlignedBytes(public_key, sizeof(public_key)); - - if (rcs->publicKeyMode==PKM_ACCEPT_ANY_PUBLIC_KEY) - { - memcpy(rcs->remote_public_key, public_key, cat::EasyHandshake::PUBLIC_KEY_BYTES); - if (!rcs->client_handshake->Initialize(public_key) || - !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) - { - CAT_AUDIT_PRINTF("AUDIT: Server passed a bad public key with PKM_ACCEPT_ANY_PUBLIC_KEY"); - rakPeer->requestedConnectionQueueMutex.Unlock(); - return true; - } - } - - if (cat::SecureEqual(public_key, - rcs->remote_public_key, - cat::EasyHandshake::PUBLIC_KEY_BYTES)==false) - { - rakPeer->requestedConnectionQueueMutex.Unlock(); - CAT_AUDIT_PRINTF("AUDIT: Expected public key does not match what was sent by server -- Reporting back ID_PUBLIC_KEY_MISMATCH to user\n"); - - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_PUBLIC_KEY_MISMATCH; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=serverGuid; - rakPeer->AddPacketToProducer(packet); - return true; - } - - if (rcs->client_handshake==0) - { - // Message does not contain a challenge - // We might still pass if we are in the security exception list - bsOut.Write((unsigned char)0); - } - else - { - // Message contains a challenge - bsOut.Write((unsigned char)1); - // challenge - CAT_AUDIT_PRINTF("AUDIT: Sending challenge\n"); - bsOut.WriteAlignedBytes((const unsigned char*) rcs->handshakeChallenge,cat::EasyHandshake::CHALLENGE_BYTES); - } -#else // LIBCAT_SECURITY - // Message does not contain a challenge - bsOut.Write((unsigned char)0); -#endif // LIBCAT_SECURITY - } - else - { - // Server does not need security -#if LIBCAT_SECURITY==1 - if (rcs->client_handshake!=0) - { - rakPeer->requestedConnectionQueueMutex.Unlock(); - CAT_AUDIT_PRINTF("AUDIT: Security disabled by server but we expected security (indicated by client_handshake not null) so failing!\n"); - - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_OUR_SYSTEM_REQUIRES_SECURITY; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=serverGuid; - rakPeer->AddPacketToProducer(packet); - return true; - } -#endif // LIBCAT_SECURITY - - } - - uint16_t mtu; - bsIn.Read(mtu); - - // Binding address - bsOut.Write(rcs->systemAddress); - rakPeer->requestedConnectionQueueMutex.Unlock(); - // MTU - bsOut.Write(mtu); - // Our guid - bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), rcs->systemAddress); - - // SocketLayer::SendTo( rakPeer->socketList[rcs->socketIndex], (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), rcs->systemAddress, _FILE_AND_LINE_ ); - - RNS2_SendParameters bsp; - bsp.data = (char*) bsOut.GetData(); - bsp.length = bsOut.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - - return true; - } - } - rakPeer->requestedConnectionQueueMutex.Unlock(); - } - else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_2) - { - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - MafiaNet::BitStream bs((unsigned char*) data,length,false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - RakNetGUID guid; - bs.Read(guid); - SystemAddress bindingAddress; - [[maybe_unused]] bool b = bs.Read(bindingAddress); // only checked by RakAssert (debug builds) - RakAssert(b); - uint16_t mtu; - b=bs.Read(mtu); - RakAssert(b); - bool doSecurity=false; - b=bs.Read(doSecurity); - RakAssert(b); - -#if LIBCAT_SECURITY==1 - char answer[cat::EasyHandshake::ANSWER_BYTES]; - CAT_AUDIT_PRINTF("AUDIT: Got ID_OPEN_CONNECTION_REPLY_2 and given doSecurity=%i\n", (int)doSecurity); - if (doSecurity) - { - CAT_AUDIT_PRINTF("AUDIT: Reading cookie and public key\n"); - bs.ReadAlignedBytes((unsigned char*) answer, sizeof(answer)); - } - cat::ClientEasyHandshake *client_handshake=0; -#endif // LIBCAT_SECURITY - - RakPeer::RequestedConnectionStruct *rcs; - bool unlock=true; - rakPeer->requestedConnectionQueueMutex.Lock(); - for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) - { - rcs=rakPeer->requestedConnectionQueue[i]; - - - if (rcs->systemAddress==systemAddress) - { -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: System address matches an entry in the requestedConnectionQueue and doSecurity=%i\n", (int)doSecurity); - if (doSecurity) - { - if (rcs->client_handshake==0) - { - CAT_AUDIT_PRINTF("AUDIT: Server wants security but we didn't set a public key -- Reporting back ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY to user\n"); - rakPeer->requestedConnectionQueueMutex.Unlock(); - - packet=rakPeer->AllocPacket(2, _FILE_AND_LINE_); - packet->data[ 0 ] = ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY; // Attempted a connection and couldn't - packet->data[ 1 ] = 0; // Indicate server public key is missing - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=guid; - rakPeer->AddPacketToProducer(packet); - return true; - } - - CAT_AUDIT_PRINTF("AUDIT: Looks good, preparing to send challenge to server! client_handshake = %x\n", client_handshake); - } - -#endif // LIBCAT_SECURITY - - rakPeer->requestedConnectionQueueMutex.Unlock(); - unlock=false; - - RakAssert(rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT); - // You might get this when already connected because of cross-connections - bool thisIPConnectedRecently=false; - remoteSystem=rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - if (remoteSystem==0) - { - if (rcs->socket == 0) - { - remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); - } - else - { - remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rcs->socket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); - } - } - - // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots - // Ignore connection attempts if this IP address connected within the last 100 milliseconds - if (thisIPConnectedRecently==false) - { - // Don't check GetRemoteSystemFromGUID, server will verify - if (remoteSystem) - { - // Move pointer from RequestedConnectionStruct to RemoteSystemStruct -#if LIBCAT_SECURITY==1 - cat::u8 ident[cat::EasyHandshake::IDENTITY_BYTES]; - bool doIdentity = false; - - if (rcs->client_handshake) - { - CAT_AUDIT_PRINTF("AUDIT: Processing answer\n"); - if (rcs->publicKeyMode == PKM_USE_TWO_WAY_AUTHENTICATION) - { - if (!rcs->client_handshake->ProcessAnswerWithIdentity(answer, ident, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) - { - CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); - return true; - } - - doIdentity = true; - } - else - { - if (!rcs->client_handshake->ProcessAnswer(answer, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) - { - CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); - return true; - } - } - CAT_AUDIT_PRINTF("AUDIT: Success!\n"); - - MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); - rcs->client_handshake=0; - } -#endif // LIBCAT_SECURITY - - remoteSystem->weInitiatedTheConnection=true; - remoteSystem->connectMode=RakPeer::RemoteSystemStruct::REQUESTED_CONNECTION; - if (rcs->timeoutTime!=0) - remoteSystem->reliabilityLayer.SetTimeoutTime(rcs->timeoutTime); - - MafiaNet::BitStream temp; - temp.Write( (MessageID)ID_CONNECTION_REQUEST); - temp.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - temp.Write(MafiaNet::GetTime()); - -#if LIBCAT_SECURITY==1 - temp.Write((unsigned char)(doSecurity ? 1 : 0)); - - if (doSecurity) - { - unsigned char proof[32]; - remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->GenerateProof(proof, sizeof(proof)); - temp.WriteAlignedBytes(proof, sizeof(proof)); - - temp.Write((unsigned char)(doIdentity ? 1 : 0)); - - if (doIdentity) - { - temp.WriteAlignedBytes(ident, sizeof(ident)); - } - } -#else - temp.Write((unsigned char)0); -#endif // LIBCAT_SECURITY - - if ( rcs->outgoingPasswordLength > 0 ) - temp.Write( ( char* ) rcs->outgoingPassword, rcs->outgoingPasswordLength ); - - rakPeer->SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, timeRead, 0 ); - } - else - { - // Failed, no connections available anymore - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - packet->guid=guid; - rakPeer->AddPacketToProducer(packet); - } - } - - rakPeer->requestedConnectionQueueMutex.Lock(); - for (unsigned int k=0; k < rakPeer->requestedConnectionQueue.Size(); k++) - { - if (rakPeer->requestedConnectionQueue[k]->systemAddress==systemAddress) - { - rakPeer->requestedConnectionQueue.RemoveAtIndex(k); - break; - } - } - rakPeer->requestedConnectionQueueMutex.Unlock(); - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Deleting client_handshake object %x and rcs->client_handshake object %x\n", client_handshake, rcs->client_handshake); - MafiaNet::OP_DELETE(client_handshake,_FILE_AND_LINE_); - MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); -#endif // LIBCAT_SECURITY - MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); - - break; - } - } - - if (unlock) - rakPeer->requestedConnectionQueueMutex.Unlock(); - - return true; - - } - else if ((unsigned char)(data)[0] == (MessageID)ID_CONNECTION_ATTEMPT_FAILED || - (unsigned char)(data)[0] == (MessageID)ID_NO_FREE_INCOMING_CONNECTIONS || - (unsigned char)(data)[0] == (MessageID)ID_CONNECTION_BANNED || - (unsigned char)(data)[0] == (MessageID)ID_ALREADY_CONNECTED || - (unsigned char)(data)[0] == (MessageID)ID_INVALID_PASSWORD || - (unsigned char)(data)[0] == (MessageID)ID_IP_RECENTLY_CONNECTED || - (unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) - { - - MafiaNet::BitStream bs((unsigned char*) data,length,false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - if ((unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) - bs.IgnoreBytes(sizeof(unsigned char)); - - RakNetGUID guid; - bs.Read(guid); - - RakPeer::RequestedConnectionStruct *rcs; - bool connectionAttemptCancelled=false; - rakPeer->requestedConnectionQueueMutex.Lock(); - for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) - { - rcs=rakPeer->requestedConnectionQueue[i]; - if (rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT && rcs->systemAddress==systemAddress) - { - connectionAttemptCancelled=true; - rakPeer->requestedConnectionQueue.RemoveAtIndex(i); - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Connection attempt canceled so deleting rcs->client_handshake object %x\n", rcs->client_handshake); - MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); -#endif // LIBCAT_SECURITY - MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); - break; - } - } - - rakPeer->requestedConnectionQueueMutex.Unlock(); - - if (connectionAttemptCancelled) - { - // Tell user of connection attempt failed - packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = data[0]; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = systemAddress; - packet->guid=guid; - rakPeer->AddPacketToProducer(packet); - } - } - else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_1 && length > (int) (1+sizeof(OFFLINE_MESSAGE_DATA_ID))) - {/* - static int x = 0; - ++x; - - SystemAddress *addr = (SystemAddress*)&systemAddress; - addr->binaryAddress += x;*/ - - //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); - char remoteProtocol=data[1+sizeof(OFFLINE_MESSAGE_DATA_ID)]; - if (remoteProtocol!=RAKNET_PROTOCOL_VERSION) - { - MafiaNet::BitStream bs; - bs.Write((MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION); - bs.Write((unsigned char)RAKNET_PROTOCOL_VERSION); - bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*)bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); - - // SocketLayer::SendTo( rakNetSocket, (char*)bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - - RNS2_SendParameters bsp; - bsp.data = (char*) bs.GetData(); - bsp.length = bs.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - return true; - } - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); - - MafiaNet::BitStream bsOut; - bsOut.Write((MessageID)ID_OPEN_CONNECTION_REPLY_1); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); -#if LIBCAT_SECURITY==1 - if (rakPeer->_using_security) - { - bsOut.Write((unsigned char) 1); // HasCookie Yes - // Write cookie - uint32_t cookie = rakPeer->_cookie_jar->Generate(&systemAddress.address,sizeof(systemAddress.address)); - CAT_AUDIT_PRINTF("AUDIT: Writing cookie %i to %i:%i\n", cookie, systemAddress); - bsOut.Write(cookie); - // Write my public key - bsOut.WriteAlignedBytes((const unsigned char *) rakPeer->my_public_key,sizeof(rakPeer->my_public_key)); - } - else -#endif // LIBCAT_SECURITY - bsOut.Write((unsigned char) 0); // HasCookie oN - - // MTU. Lower MTU if it is exceeds our own limit - if (length+UDP_HEADER_SIZE > MAXIMUM_MTU_SIZE) - bsOut.WriteCasted(MAXIMUM_MTU_SIZE); - else - bsOut.WriteCasted(length+UDP_HEADER_SIZE); - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - // SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - - RNS2_SendParameters bsp; - bsp.data = (char*) bsOut.GetData(); - bsp.length = bsOut.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - } - else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_2) - { - SystemAddress bindingAddress; - RakNetGUID guid; - MafiaNet::BitStream bsOut; - MafiaNet::BitStream bs((unsigned char*) data, length, false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - - bool requiresSecurityOfThisClient=false; -#if LIBCAT_SECURITY==1 - char remoteHandshakeChallenge[cat::EasyHandshake::CHALLENGE_BYTES]; - - if (rakPeer->_using_security) - { - systemAddress.ToString(false, str1, static_cast(64)); - requiresSecurityOfThisClient=rakPeer->IsInSecurityExceptionList(str1)==false; - - uint32_t cookie; - bs.Read(cookie); - CAT_AUDIT_PRINTF("AUDIT: Got cookie %i from %i:%i\n", cookie, systemAddress); - if (rakPeer->_cookie_jar->Verify(&systemAddress.address,sizeof(systemAddress.address), cookie)==false) - { - return true; - } - CAT_AUDIT_PRINTF("AUDIT: Cookie good!\n"); - - unsigned char clientWroteChallenge; - bs.Read(clientWroteChallenge); - - if (requiresSecurityOfThisClient==true && clientWroteChallenge==0) - { - // Fail, client doesn't support security, and it is required - return true; - } - - if (clientWroteChallenge) - { - bs.ReadAlignedBytes((unsigned char*) remoteHandshakeChallenge, cat::EasyHandshake::CHALLENGE_BYTES); -#ifdef CAT_AUDIT - printf("AUDIT: RECV CHALLENGE "); - for (int ii = 0; ii < sizeof(remoteHandshakeChallenge); ++ii) - { - printf("%02x", (cat::u8)remoteHandshakeChallenge[ii]); - } - printf("\n"); -#endif - } - } -#endif // LIBCAT_SECURITY - - bs.Read(bindingAddress); - uint16_t mtu; - bs.Read(mtu); - bs.Read(guid); - - RakPeer::RemoteSystemStruct *rssFromSA = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - bool IPAddrInUse = rssFromSA != 0 && rssFromSA->isActive; - RakPeer::RemoteSystemStruct *rssFromGuid = rakPeer->GetRemoteSystemFromGUID(guid, true); - bool GUIDInUse = rssFromGuid != 0 && rssFromGuid->isActive; - - // IPAddrInUse, GuidInUse, outcome - // TRUE, , TRUE , ID_OPEN_CONNECTION_REPLY if they are the same, else ID_ALREADY_CONNECTED - // FALSE, , TRUE , ID_ALREADY_CONNECTED (someone else took this guid) - // TRUE, , FALSE , ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) - // FALSE , FALSE , Allow connection - - int outcome; - if (IPAddrInUse & GUIDInUse) - { - if (rssFromSA==rssFromGuid && rssFromSA->connectMode==RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER) - { - // ID_OPEN_CONNECTION_REPLY if they are the same - outcome=1; - - // Note to self: If REQUESTED_CONNECTION, this means two systems attempted to connect to each other at the same time, and one finished first. - // Returns ID)_CONNECTION_REQUEST_ACCEPTED to one system, and ID_ALREADY_CONNECTED followed by ID_NEW_INCOMING_CONNECTION to another - } - else - { - // ID_ALREADY_CONNECTED (restarted raknet, connected again from same ip, plus someone else took this guid) - outcome=2; - } - } - else if (IPAddrInUse==false && GUIDInUse==true) - { - // ID_ALREADY_CONNECTED (someone else took this guid) - outcome=3; - } - else if (IPAddrInUse==true && GUIDInUse==false) - { - // ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) - outcome=4; - } - else - { - // Allow connection - outcome=0; - } - - MafiaNet::BitStream bsAnswer; - bsAnswer.Write((MessageID)ID_OPEN_CONNECTION_REPLY_2); - bsAnswer.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsAnswer.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); - bsAnswer.Write(systemAddress); - bsAnswer.Write(mtu); - bsAnswer.Write(requiresSecurityOfThisClient); - - if (outcome==1) - { - // Duplicate connection request packet from packetloss - // Send back the same answer -#if LIBCAT_SECURITY==1 - if (requiresSecurityOfThisClient) - { - CAT_AUDIT_PRINTF("AUDIT: Resending public key and answer from packetloss. Sending ID_OPEN_CONNECTION_REPLY_2\n"); - bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); - } -#endif // LIBCAT_SECURITY - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); - // SocketLayer::SendTo( rakNetSocket, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - - RNS2_SendParameters bsp; - bsp.data = (char*) bsAnswer.GetData(); - bsp.length = bsAnswer.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - - return true; - } - else if (outcome!=0) - { - bsOut.Write((MessageID)ID_ALREADY_CONNECTED); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(rakPeer->myGuid); - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - // SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - RNS2_SendParameters bsp; - bsp.data = (char*) bsOut.GetData(); - bsp.length = bsOut.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - - return true; - } - - if (rakPeer->AllowIncomingConnection(systemAddress)==false) - { - bsOut.Write((MessageID)ID_NO_FREE_INCOMING_CONNECTIONS); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(rakPeer->myGuid); - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - //SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - RNS2_SendParameters bsp; - bsp.data = (char*) bsOut.GetData(); - bsp.length = bsOut.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - - return true; - } - - bool thisIPConnectedRecently=false; - rssFromSA = rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, requiresSecurityOfThisClient); - - if (thisIPConnectedRecently==true) - { - bsOut.Write((MessageID)ID_IP_RECENTLY_CONNECTED); - bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bsOut.Write(rakPeer->myGuid); - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); - //SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - - RNS2_SendParameters bsp; - bsp.data = (char*) bsOut.GetData(); - bsp.length = bsOut.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - - return true; - } - -#if LIBCAT_SECURITY==1 - if (requiresSecurityOfThisClient) - { - CAT_AUDIT_PRINTF("AUDIT: Writing public key. Sending ID_OPEN_CONNECTION_REPLY_2\n"); - if (rakPeer->_server_handshake->ProcessChallenge(remoteHandshakeChallenge, rssFromSA->answer, rssFromSA->reliabilityLayer.GetAuthenticatedEncryption() )) - { - CAT_AUDIT_PRINTF("AUDIT: Challenge good!\n"); - // Keep going to OK block - } - else - { - CAT_AUDIT_PRINTF("AUDIT: Challenge BAD!\n"); - - // Unassign this remote system - rakPeer->DereferenceRemoteSystem(systemAddress); - return true; - } - - bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); - } -#endif // LIBCAT_SECURITY - - for (i=0; i < rakPeer->pluginListNTS.Size(); i++) - rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); - // SocketLayer::SendTo( rakNetSocket, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); - RNS2_SendParameters bsp; - bsp.data = (char*) bsAnswer.GetData(); - bsp.length = bsAnswer.GetNumberOfBytesUsed(); - bsp.systemAddress = systemAddress; - rakNetSocket->Send(&bsp, _FILE_AND_LINE_); - } - return true; - } - - return false; -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -// #med - deprecate this overload --- socketList[0] is not really ensured to be the correctly bound socket at all! -void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, MafiaNet::TimeUS timeRead, BitStream &updateBitStream ) -{ - ProcessNetworkPacket(systemAddress,data,length,rakPeer,rakPeer->socketList[0],timeRead, updateBitStream); -} -void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSocket2* rakNetSocket, MafiaNet::TimeUS timeRead, BitStream &updateBitStream ) -{ -#if LIBCAT_SECURITY==1 -#ifdef CAT_AUDIT - printf("AUDIT: RECV "); - for (int ii = 0; ii < length; ++ii) - { - printf("%02x", (cat::u8)data[ii]); - } - printf("\n"); -#endif -#endif // LIBCAT_SECURITY - - RakAssert(systemAddress.GetPort()); - bool isOfflineMessage; - if (ProcessOfflineNetworkPacket(systemAddress, data, length, rakPeer, rakNetSocket, &isOfflineMessage, timeRead)) - { - return; - } - -// MafiaNet::Packet *packet; - RakPeer::RemoteSystemStruct *remoteSystem; - - // See if this datagram came from a connected system - remoteSystem = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); - if ( remoteSystem ) - { - // Handle regular incoming data - // HandleSocketReceiveFromConnectedPlayer is only safe to be called from the same thread as Update, which is this thread - if ( isOfflineMessage==false) - { - remoteSystem->reliabilityLayer.HandleSocketReceiveFromConnectedPlayer( - data, length, systemAddress, rakPeer->pluginListNTS, remoteSystem->MTUSize, - rakNetSocket, &rnr, timeRead, updateBitStream); - } - } - else - { - // int a=5; - // printf("--- Packet from unknown system %s\n", systemAddress.ToString()); - } -} - -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GenerateSeedFromGuid(void) -{ - /* - // Construct a random seed based on the initial guid value, and the last digits of the difference to each subsequent number - // This assumes that only the last 3 bits of each guidId integer has a meaningful amount of randomness between it and the prior number - unsigned int t = guid.g[0]; - unsigned int i; - for (i=1; i < sizeof(guid.g) / sizeof(guid.g[0]); i++) - { - unsigned int diff = guid.g[i]-guid.g[i-1]; - unsigned int diff3Bits = diff & 0x0007; - diff3Bits <<= 29; - diff3Bits >>= (i-1)*3; - t ^= diff3Bits; - } - - return t; - */ - return (unsigned int) ((myGuid.g >> 32) ^ myGuid.g); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -void RakPeer::DerefAllSockets(void) -{ - unsigned int i; - for (i=0; i < socketList.Size(); i++) - { - if (socketFactory && !socketList[i]->IsBerkleySocket()) - socketFactory->Destroy(socketList[i]); - else - MafiaNet::OP_DELETE(socketList[i], _FILE_AND_LINE_); - } - socketList.Clear(false, _FILE_AND_LINE_); -} -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -unsigned int RakPeer::GetRakNetSocketFromUserConnectionSocketIndex(unsigned int userIndex) const -{ - unsigned int i; - for (i=0; i < socketList.Size(); i++) - { - if (socketList[i]->GetUserConnectionSocketIndex()==userIndex) - return i; - } - RakAssert("GetRakNetSocketFromUserConnectionSocketIndex failed" && 0); - return (unsigned int) -1; -} - -/* -// DS_APR -void RakPeer::ProcessChromePacket(RakNetSocket2 *s, const char *buffer, int dataSize, const SystemAddress& recvFromAddress, MafiaNet::TimeUS timeRead) -{ - RakAssert(buffer); - RakAssert(dataSize > 0); - RakAssert(recvFromAddress.GetPort()); - - RNS2RecvStruct *recvFromStruct; - recvFromStruct=bufferedPackets.Allocate( _FILE_AND_LINE_ ); - RakAssert(dataSize <= (int)sizeof(recvFromStruct->data)); - memcpy(recvFromStruct->data, buffer, dataSize); - recvFromStruct->bytesRead=dataSize; - recvFromStruct->systemAddress=recvFromAddress; - recvFromStruct->timeRead=timeRead; - bufferedPackets.Push(recvFromStruct); -} -*/ - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -/* -bool RakPeer::RunRecvFromOnce( RakNetSocket *s ) -{ - RakPeer::RecvFromStruct *recvFromStruct; - - recvFromStruct=bufferedPackets.Allocate( _FILE_AND_LINE_ ); - if (recvFromStruct != nullptr) - { - recvFromStruct->s=s; - SocketLayer::RecvFromBlocking(s, this, recvFromStruct->data, &recvFromStruct->bytesRead, &recvFromStruct->systemAddress, &recvFromStruct->timeRead); - - if (recvFromStruct->bytesRead>0) - { - RakAssert(recvFromStruct->systemAddress.GetPort()); - bufferedPackets.Push(recvFromStruct); - quitAndDataEvents.SetEvent(); - - // Got data - return true; - } - else - { - bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); - } - } - // No data - return false; -} -*/ -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -bool RakPeer::RunUpdateCycle(BitStream &updateBitStream ) -{ - RakPeer::RemoteSystemStruct * remoteSystem; - unsigned int activeSystemListIndex; - Packet *packet; - // int currentSentBytes,currentReceivedBytes; -// unsigned numberOfBytesUsed; -// BitSize_t numberOfBitsUsed; - //SystemAddress authoritativeClientSystemAddress; - BitSize_t bitSize; - unsigned int byteSize; - unsigned char *data; - SystemAddress systemAddress; - BufferedCommandStruct *bcs; - bool callerDataAllocationUsed; - RakNetStatistics *rnss; - MafiaNet::TimeUS timeNS=0; - MafiaNet::Time timeMS=0; - - // This is here so RecvFromBlocking actually gets data from the same thread - -#if defined(_WIN32) - // #high - review this - this is hardly correct to only work with the first socket here... - if (socketList[0]->GetSocketType()==RNS2T_WINDOWS && ((RNS2_Windows*)socketList[0])->GetSocketLayerOverride()) - { - int len; - SystemAddress sender; - char dataOut[ MAXIMUM_MTU_SIZE ]; - do { - len = ((RNS2_Windows*)socketList[0])->GetSocketLayerOverride()->RakNetRecvFrom(dataOut,&sender,true); - if (len>0) - ProcessNetworkPacket( sender, dataOut, len, this, socketList[0], MafiaNet::GetTimeUS(), updateBitStream ); - } while (len>0); - } -#endif - -// unsigned int socketListIndex; - RNS2RecvStruct *recvFromStruct; - while ((recvFromStruct=PopBufferedPacket())!=0) - { - /* - for (socketListIndex=0; socketListIndex < socketList.Size(); socketListIndex++) - { - if ((RakNetSocket*) socketList[socketListIndex]==recvFromStruct->s) - break; - } - if (socketListIndex!=socketList.Size()) - */ - ProcessNetworkPacket(recvFromStruct->systemAddress, recvFromStruct->data, recvFromStruct->bytesRead, this, recvFromStruct->socket, recvFromStruct->timeRead, updateBitStream); - DeallocRNS2RecvStruct(recvFromStruct, _FILE_AND_LINE_); - } - - while ((bcs=bufferedCommands.PopInaccurate())!=0) - { - if (bcs->command==BufferedCommandStruct::BCS_SEND) - { - // GetTime is a very slow call so do it once and as late as possible - if (timeNS==0) - { - timeNS = MafiaNet::GetTimeUS(); - timeMS = (MafiaNet::TimeMS)(timeNS/(MafiaNet::TimeUS)1000); - } - - callerDataAllocationUsed=SendImmediate((char*)bcs->data, bcs->numberOfBitsToSend, bcs->priority, bcs->reliability, bcs->orderingChannel, bcs->systemIdentifier, bcs->broadcast, true, timeNS, bcs->receipt); - if ( callerDataAllocationUsed==false ) - rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); - - // Set the new connection state AFTER we call sendImmediate in case we are setting it to a disconnection state, which does not allow further sends - if (bcs->connectionMode!=RemoteSystemStruct::NO_ACTION ) - { - remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); - if (remoteSystem) - remoteSystem->connectMode=bcs->connectionMode; - } - } - else if (bcs->command==BufferedCommandStruct::BCS_SESSION_ACCEPT || bcs->command==BufferedCommandStruct::BCS_SESSION_REJECT) - { - // Validate here rather than in the user-thread API: only the network thread may read connectMode - // and the awaiting flag without racing the receive path. - RemoteSystemStruct *sessionSystem = GetRemoteSystem( bcs->systemIdentifier, true, true ); - // connectMode is checked as well as the awaiting flag: a peer that disconnects while the decision - // is queued leaves the flag set (the teardown path does not clear session state), and answering - // then would send on a dying connection and report a connection for a peer that has already gone. - if (sessionSystem && sessionSystem->isActive && - sessionSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && - sessionSystem->sessionConfigAwaitingLocalDecision) - { - sessionSystem->sessionConfigAwaitingLocalDecision = false; - const unsigned int sessionPayloadLength = (unsigned int) BITS_TO_BYTES(bcs->numberOfBitsToSend); - if (bcs->command==BufferedCommandStruct::BCS_SESSION_ACCEPT) - { - // With a pending pool the handshake may have started while the server was full, so the - // incoming limit is enforced here. The application should check first; this is the backstop - // that keeps two accepts racing one free slot from both getting it. - SessionAdmission::Limits limits; - limits.maxIncoming = GetMaximumIncomingConnections(); - limits.maxPending = maximumPendingSessions; - limits.maxPendingPerAddress = maximumPendingSessionsPerAddress; - SessionAdmission::Counts counts; - CountRemoteInitiatedPeers(UNASSIGNED_SYSTEM_ADDRESS, counts.connected, counts.exchanging, counts.pending, counts.pendingFromAddress); - if (SessionAdmission::MayAccept(limits, counts)) - SendSessionConfigResponse(sessionSystem, bcs->data, sessionPayloadLength); - else - SendSessionConfigRejection(sessionSystem, 0, 0); - } - else - SendSessionConfigRejection(sessionSystem, bcs->data, sessionPayloadLength); - } - if (bcs->data) - rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); - } - else if (bcs->command==BufferedCommandStruct::BCS_SESSION_STATUS) - { - // Only a server peer still awaiting its application's decision may be told anything, and telling - // it restarts the session timer here as well as on the client when it arrives. - RemoteSystemStruct *sessionSystem = GetRemoteSystem( bcs->systemIdentifier, true, true ); - if (sessionSystem && sessionSystem->isActive && - sessionSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && - sessionSystem->sessionConfigAwaitingLocalDecision && - sessionSystem->sessionConfigIsConnectingSide==false) - { - MafiaNet::BitStream statusStream; - statusStream.Write((MessageID)ID_SESSION_CONFIG_STATUS); - const unsigned int statusLength = (unsigned int) BITS_TO_BYTES(bcs->numberOfBitsToSend); - if (bcs->data && statusLength > 0) - statusStream.Write(bcs->data, statusLength); - SendImmediate((char*)statusStream.GetData(), statusStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, sessionSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); - sessionSystem->sessionConfigStartTime = MafiaNet::GetTimeMS(); - } - if (bcs->data) - rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); - } - else if (bcs->command==BufferedCommandStruct::BCS_CLOSE_CONNECTION) - { - // bcs->socket may be null: the socket can be released between queuing this - // command and processing it here (e.g. during rapid connect/disconnect churn). - // RakAssert is a no-op in release (NDEBUG), so guard explicitly and fall back - // to the primary socket to avoid dereferencing null in CloseConnectionInternal2. - RakNetSocket2 *closeSocket = bcs->socket; - if (closeSocket == nullptr && socketList.Size() > 0) - closeSocket = socketList[0]; - if (closeSocket != nullptr) - CloseConnectionInternal2(bcs->systemIdentifier, false, true, bcs->orderingChannel, bcs->priority, *closeSocket); - } - else if (bcs->command==BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS) - { - // Reroute - RakPeer::RemoteSystemStruct *rssFromGuid = GetRemoteSystem(bcs->systemIdentifier.rakNetGuid,true,true); - if (rssFromGuid!=0) - { - unsigned int existingSystemIndex = GetRemoteSystemIndex(rssFromGuid->systemAddress); - ReferenceRemoteSystem(bcs->systemIdentifier.systemAddress, existingSystemIndex); - } - } - else if (bcs->command==BufferedCommandStruct::BCS_GET_SOCKET) - { - SocketQueryOutput *sqo; - if (bcs->systemIdentifier.IsUndefined()) - { - sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); - sqo->sockets=socketList; - socketQueryOutput.Push(sqo); - } - else - { - remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); - sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); - - sqo->sockets.Clear(false, _FILE_AND_LINE_); - if (remoteSystem) - { - sqo->sockets.Push(remoteSystem->rakNetSocket, _FILE_AND_LINE_ ); - } - else - { - // Leave empty smart pointer - } - socketQueryOutput.Push(sqo); - } - - } - -#ifdef _DEBUG - bcs->data=0; -#endif - - bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); - } - - if (requestedConnectionQueue.IsEmpty()==false) - { - if (timeNS==0) - { - timeNS = MafiaNet::GetTimeUS(); - timeMS = (MafiaNet::TimeMS)(timeNS/(MafiaNet::TimeUS)1000); - } - - bool condition1, condition2; - unsigned requestedConnectionQueueIndex=0; - requestedConnectionQueueMutex.Lock(); - while (requestedConnectionQueueIndex < requestedConnectionQueue.Size()) - { - RequestedConnectionStruct *rcs; - rcs = requestedConnectionQueue[requestedConnectionQueueIndex]; - requestedConnectionQueueMutex.Unlock(); - if (rcs->nextRequestTime < timeMS) - { - condition1=rcs->requestsMade==rcs->sendConnectionAttemptCount+1; - condition2=(bool)((rcs->systemAddress==UNASSIGNED_SYSTEM_ADDRESS)==1); - // If too many requests made or a hole then remove this if possible, otherwise invalidate it - if (condition1 || condition2) - { - if (rcs->data) - { - rakFree_Ex(rcs->data, _FILE_AND_LINE_ ); - rcs->data=0; - } - - if (condition1 && !condition2 && rcs->actionToTake==RequestedConnectionStruct::CONNECT) - { - // Tell user of connection attempt failed - packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); - packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't - packet->bitSize = ( sizeof( char ) * 8); - packet->systemAddress = rcs->systemAddress; - AddPacketToProducer(packet); - } - -#if LIBCAT_SECURITY==1 - CAT_AUDIT_PRINTF("AUDIT: Connection attempt FAILED so deleting rcs->client_handshake object %x\n", rcs->client_handshake); - MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); -#endif - MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); - - requestedConnectionQueueMutex.Lock(); - for (unsigned int k=0; k < requestedConnectionQueue.Size(); k++) - { - if (requestedConnectionQueue[k]==rcs) - { - requestedConnectionQueue.RemoveAtIndex(k); - break; - } - } - requestedConnectionQueueMutex.Unlock(); - } - else - { - - const unsigned attemptsPerMTU = GetConnectionAttemptsPerMTU(rcs->sendConnectionAttemptCount); - int MTUSizeIndex = rcs->requestsMade / attemptsPerMTU; - if (MTUSizeIndex>=NUM_MTU_SIZES) - MTUSizeIndex=NUM_MTU_SIZES-1; - rcs->requestsMade++; - rcs->nextRequestTime=timeMS+rcs->timeBetweenSendConnectionAttemptsMS; - - MafiaNet::BitStream bitStream; - //WriteOutOfBandHeader(&bitStream, ID_USER_PACKET_ENUM); - bitStream.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_1); - bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); - bitStream.Write((MessageID)RAKNET_PROTOCOL_VERSION); - bitStream.PadWithZeroToByteLength(mtuSizes[MTUSizeIndex]-UDP_HEADER_SIZE); - - char str[256]; - rcs->systemAddress.ToString(true,str,static_cast(256)); - - //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); - - unsigned i; - for (i=0; i < pluginListNTS.Size(); i++) - pluginListNTS[i]->OnDirectSocketSend((const char*) bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), rcs->systemAddress); - - RakNetSocket2 *socketToUse; - if (rcs->socket == 0) - socketToUse = socketList[rcs->socketIndex]; - else - socketToUse = rcs->socket; - - rcs->systemAddress.FixForIPVersion(socketToUse->GetBoundAddress()); - if (socketToUse->IsBerkleySocket()) - ((RNS2_Berkley*)socketToUse)->SetDoNotFragment(1); - -// SocketLayer::SetDoNotFragment(socketToUse, 1); - MafiaNet::Time sendToStart= MafiaNet::GetTime(); - - RNS2_SendParameters bsp; - bsp.data = (char*) bitStream.GetData(); - bsp.length = bitStream.GetNumberOfBytesUsed(); - bsp.systemAddress = rcs->systemAddress; - const RNS2SendResult sendResult = socketToUse->Send(&bsp, _FILE_AND_LINE_); - // Send() reports failure as sendto's own return value and drops the reason, so - // the error has to be read back off the thread before anything else touches a - // socket. The old test here compared that return value against 10040 directly, - // which sendto never yields -- it yields SOCKET_ERROR -- so this branch was dead - // and a locally-refused MTU burned its rung's entire attempt budget. - if (sendResult < 0 && RNS2_IsDatagramTooLargeError(RNS2_GetLastSocketError())) - { - // The local interface refused the datagram outright. Every attempt at this - // size fails the same way, so step down now. - rcs->requestsMade = (unsigned char) ((MTUSizeIndex + 1) * attemptsPerMTU); - rcs->nextRequestTime=timeMS; - } - else - { - MafiaNet::Time sendToEnd= MafiaNet::GetTime(); - if (sendToEnd-sendToStart>100) - { - // A send that blocks this long means the outgoing interface is - // struggling: a virtual VPN adapter renegotiating its tunnel, or a full - // transmit queue. Smaller datagrams are likelier to get through, so drop - // to the lowest MTU -- but keep trying. Giving up here (which is what - // this did when already on the lowest rung) turned a transient stall - // into ID_CONNECTION_ATTEMPT_FAILED with most of the budget unspent. - const int lowestMtuIndex = (int) (attemptsPerMTU * (unsigned) (NUM_MTU_SIZES - 1)); - if (lowestMtuIndex > (int) rcs->requestsMade) - { - rcs->requestsMade = (unsigned char) lowestMtuIndex; - rcs->nextRequestTime=timeMS; - } - } - } - // SocketLayer::SetDoNotFragment(socketToUse, 0); - if (socketToUse->IsBerkleySocket()) - ((RNS2_Berkley*)socketToUse)->SetDoNotFragment(0); - - requestedConnectionQueueIndex++; - } - } - else - requestedConnectionQueueIndex++; - - requestedConnectionQueueMutex.Lock(); - } - requestedConnectionQueueMutex.Unlock(); - } - - // remoteSystemList in network thread - for ( activeSystemListIndex = 0; activeSystemListIndex < activeSystemListSize; ++activeSystemListIndex ) - //for ( remoteSystemIndex = 0; remoteSystemIndex < remoteSystemListSize; ++remoteSystemIndex ) - { - // I'm using systemAddress from remoteSystemList but am not locking it because this loop is called very frequently and it doesn't - // matter if we miss or do an extra update. The reliability layers themselves never care which player they are associated with - //systemAddress = remoteSystemList[ remoteSystemIndex ].systemAddress; - // Allow the systemAddress for this remote system list to change. We don't care if it changes now. - // remoteSystemList[ remoteSystemIndex ].allowSystemAddressAssigment=true; - - - // Found an active remote system - remoteSystem = activeSystemList[ activeSystemListIndex ]; - systemAddress = remoteSystem->systemAddress; - RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); - // Update is only safe to call from the same thread that calls HandleSocketReceiveFromConnectedPlayer, - // which is this thread - - if (timeNS==0) - { - timeNS = MafiaNet::GetTimeUS(); - timeMS = (MafiaNet::TimeMS)(timeNS/(MafiaNet::TimeUS)1000); - //RAKNET_DEBUG_PRINTF("timeNS = %I64i timeMS=%i\n", timeNS, timeMS); - } - - - if (timeMS > remoteSystem->lastReliableSend && timeMS-remoteSystem->lastReliableSend > remoteSystem->reliabilityLayer.GetTimeoutTime()/2 && remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) - { - // If no reliable packets are waiting for an ack, do a one byte reliable send so that disconnections are noticed - RakNetStatistics rakNetStatistics; - rnss=remoteSystem->reliabilityLayer.GetStatistics(&rakNetStatistics); - if (rnss->messagesInResendBuffer==0) - { - PingInternal( systemAddress, true, MafiaNet::Reliability::Reliable ); - - //remoteSystem->lastReliableSend=timeMS+remoteSystem->reliabilityLayer.GetTimeoutTime(); - remoteSystem->lastReliableSend=timeMS; - } - } - - if (endThreads) - // for the final call, make sure we send out any outstanding ACKs - remoteSystem->reliabilityLayer.UpdateAndForceACKs( remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream ); // systemAddress only used for the internet simulator test - else - remoteSystem->reliabilityLayer.Update( remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream ); // systemAddress only used for the internet simulator test - - // The reliability layer steps the negotiated MTU down when it - // detects an in-session path-MTU black hole; keep the value - // GetMTUSize() reports in sync with what is actually on the wire. - // Written only on an actual step-down: GetMTUSize() reads this - // field from the user thread without synchronization (as it always - // has for the connect-time write), so avoid turning it into a - // continuously-written field. - const int reliabilityMtu = remoteSystem->reliabilityLayer.GetCurrentMtuBytes(); - if (remoteSystem->MTUSize != reliabilityMtu) - remoteSystem->MTUSize = reliabilityMtu; - - // Check for failure conditions - if ( remoteSystem->reliabilityLayer.IsDeadConnection() || - ((remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY) && remoteSystem->reliabilityLayer.IsOutgoingDataWaiting()==false) || - (remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK && (remoteSystem->reliabilityLayer.AreAcksWaiting()==false || remoteSystem->reliabilityLayer.AckTimeout(timeMS)==true)) || - (( - (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || - remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || - remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) - && timeMS > remoteSystem->connectionTime && timeMS - remoteSystem->connectionTime > 10000)) - ) - { - // RAKNET_DEBUG_PRINTF("timeMS=%i remoteSystem->connectionTime=%i\n", timeMS, remoteSystem->connectionTime ); - - // Before the close notification logic: a held session request is not a reported connection, but - // the application is holding a decision for it and has to learn it will never be needed. - ProduceSessionAbandoned(remoteSystem); - - // Failed. Inform the user? - // TODO - RakNet 4.0 - Return a different message identifier for DISCONNECT_ASAP_SILENTLY and DISCONNECT_ASAP than for DISCONNECT_ON_NO_ACK - // The first two mean we called CloseConnection(), the last means the other system sent us ID_DISCONNECTION_NOTIFICATION - // A connection the application was never told about must not produce a close notification for - // it. On the accepting side that covers a peer refused by RejectSession() (which parks the slot - // in DISCONNECT_ON_NO_ACK so the refusal is delivered first) and one dropped mid-handshake. - // The connecting side is exempt: it has an outstanding Connect() to resolve either way. - const bool reportableConnection = remoteSystem->connectionReportedToApplication || remoteSystem->weInitiatedTheConnection; - if (reportableConnection && - (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED || remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION - || remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA - || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK)) - { - -// MafiaNet::BitStream undeliveredMessages; -// remoteSystem->reliabilityLayer.GetUndeliveredMessages(&undeliveredMessages,remoteSystem->MTUSize); - -// packet=AllocPacket(sizeof( char ) + undeliveredMessages.GetNumberOfBytesUsed()); - // A graceful remote disconnect (DISCONNECT_ON_NO_ACK) may carry a reason payload stashed when the - // notification arrived. Only ID_DISCONNECTION_NOTIFICATION carries it; ID_CONNECTION_LOST and - // ID_CONNECTION_ATTEMPT_FAILED are locally synthesized and stay payload-less. - const bool attachReason = remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK && remoteSystem->disconnectReasonData!=0; - const unsigned int reasonLength = attachReason ? remoteSystem->disconnectReasonLength : 0; - packet=AllocPacket(sizeof( char ) + reasonLength, _FILE_AND_LINE_); - if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) - packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't - else if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA) - // Dropped mid-session-handshake. The connecting side still has an attempt outstanding; the - // accepting side never reported anything, so a lost connection is all it can report. - packet->data[ 0 ] = remoteSystem->sessionConfigIsConnectingSide - ? (unsigned char)ID_CONNECTION_ATTEMPT_FAILED : (unsigned char)ID_CONNECTION_LOST; - else if (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) - packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection - else - packet->data[ 0 ] = ID_DISCONNECTION_NOTIFICATION; // DeadConnection - - if (attachReason) - memcpy(packet->data + sizeof(unsigned char), remoteSystem->disconnectReasonData, reasonLength); - -// memcpy(packet->data+1, undeliveredMessages.GetData(), undeliveredMessages.GetNumberOfBytesUsed()); - - packet->guid = remoteSystem->guid; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - - AddPacketToProducer(packet); - } - // else connection shutting down, don't bother telling the user - -#ifdef _DO_PRINTF - RAKNET_DEBUG_PRINTF("Connection dropped for player %i:%i\n", systemAddress); -#endif - // we are about to close the connection to the remote system, we'd still make sure to send any outstanding ACKs, so for the remote system not unnecessarily waiting for these until its timeout - // (and trying to resend them unnecessarily) - remoteSystem->reliabilityLayer.UpdateAndForceACKs(remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream); - - CloseConnectionInternal2(systemAddress, false, true, 0, MafiaNet::Priority::Low, *(remoteSystem->rakNetSocket)); - continue; - } - - // A session handshake that never completes must not pin the slot. Acks keep flowing in - // EXCHANGING_SESSION_DATA, so the ordinary dead-connection detection above would never fire; bound - // it by the same timeout instead. Interactive servers put an application in the middle of this - // exchange, so it is a reachable state, not a theoretical one. - if ( remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && - remoteSystem->sessionConfigStartTime!=0 && - timeMS > remoteSystem->sessionConfigStartTime && - timeMS - remoteSystem->sessionConfigStartTime > GetSessionTimeoutFor(remoteSystem) ) - { - // Only the connecting side reports a failure; a server just drops the half-open slot. - if (remoteSystem->sessionConfigIsConnectingSide) - { - Packet *timeoutPacket=AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); - timeoutPacket->data[0]=(unsigned char)ID_CONNECTION_ATTEMPT_FAILED; - timeoutPacket->bitSize=sizeof(MessageID)*8; - timeoutPacket->systemAddress=remoteSystem->systemAddress; - timeoutPacket->systemAddress.systemIndex=remoteSystem->remoteSystemIndex; - timeoutPacket->guid=remoteSystem->guid; - timeoutPacket->guid.systemIndex=timeoutPacket->systemAddress.systemIndex; - AddPacketToProducer(timeoutPacket); - } - else - { - ProduceSessionAbandoned(remoteSystem); - } - - remoteSystem->reliabilityLayer.UpdateAndForceACKs(remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream); - CloseConnectionInternal2(systemAddress, false, true, 0, MafiaNet::Priority::Low, *(remoteSystem->rakNetSocket)); - continue; - } - - // Ping this guy if it is time to do so - if ( remoteSystem->connectMode==RemoteSystemStruct::CONNECTED && timeMS > remoteSystem->nextPingTime && ( occasionalPing || remoteSystem->lowestPing == (unsigned short)-1 ) ) - { - remoteSystem->nextPingTime = timeMS + 5000; - PingInternal( systemAddress, true, MafiaNet::Reliability::Unreliable ); - - // Update again immediately after this tick so the ping goes out right away - quitAndDataEvents.SetEvent(); - } - - // Find whoever has the lowest player ID - //if (systemAddress < authoritativeClientSystemAddress) - // authoritativeClientSystemAddress=systemAddress; - - // Does the reliability layer have any packets waiting for us? - // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer - bitSize = remoteSystem->reliabilityLayer.Receive( &data ); - - while ( bitSize > 0 ) - { - // These types are for internal use and should never arrive from a network packet - if (data[0]==ID_CONNECTION_ATTEMPT_FAILED) - { - RakAssert(0); - bitSize=0; - continue; - } - - // Fast and easy - just use the data that was returned - byteSize = (unsigned int) BITS_TO_BYTES( bitSize ); - - // For unknown senders we only accept a few specific packets - if (remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) - { - if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) - { - ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else - { - CloseConnectionInternal2(systemAddress, false, true, 0, MafiaNet::Priority::Low, *(remoteSystem->rakNetSocket)); -#ifdef _DO_PRINTF - RAKNET_DEBUG_PRINTF("Temporarily banning %i:%i for sending nonsense data\n", systemAddress); -#endif - - char str1[64]; - systemAddress.ToString(false, str1, static_cast(64)); - AddToBanList(str1, remoteSystem->reliabilityLayer.GetTimeoutTime()); - - - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else - { - // However, if we are connected we still take a connection request in case both systems are trying to connect to each other - // at the same time - if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) - { - // 04/27/06 This is wrong. With cross connections, we can both have initiated the connection are in state REQUESTED_CONNECTION - // 04/28/06 Downgrading connections from connected will close the connection due to security at ((remoteSystem->connectMode!=RemoteSystemStruct::CONNECTED && time > remoteSystem->connectionTime && time - remoteSystem->connectionTime > 10000)) - if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) - { - ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); - } - else - { - - MafiaNet::BitStream bs((unsigned char*) data,byteSize,false); - bs.IgnoreBytes(sizeof(MessageID)); - bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); - bs.IgnoreBytes(RakNetGUID::size()); - MafiaNet::Time incomingTimestamp; - bs.Read(incomingTimestamp); - - // Got a connection request message from someone we are already connected to. Just reply normally. - // This can happen due to race conditions with the fully connected mesh - OnConnectionRequest( remoteSystem, incomingTimestamp ); - } - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char) data[ 0 ] == ID_NEW_INCOMING_CONNECTION && byteSize > sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(MafiaNet::Time)*2 ) - { - if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) - { - // Transport is up, but the application is not told so until the client's session payload - // arrives in ID_SESSION_CONFIG_REQUEST and we answer it. - remoteSystem->connectMode=RemoteSystemStruct::EXCHANGING_SESSION_DATA; - remoteSystem->sessionConfigIsConnectingSide=false; - remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); - PingInternal( systemAddress, true, MafiaNet::Reliability::Unreliable ); - - // Update again immediately after this tick so the ping goes out right away - quitAndDataEvents.SetEvent(); - - MafiaNet::BitStream inBitStream((unsigned char *) data, byteSize, false); - SystemAddress bsSystemAddress; - - inBitStream.IgnoreBits(8); - inBitStream.Read(bsSystemAddress); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); - - MafiaNet::Time sendPingTime, sendPongTime; - inBitStream.Read(sendPingTime); - inBitStream.Read(sendPongTime); - OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); - - // Overwrite the data in the packet - // NewIncomingConnectionStruct newIncomingConnectionStruct; - // MafiaNet::BitStream nICS_BS( data, NewIncomingConnectionStruct_Size, false ); - // newIncomingConnectionStruct.Deserialize( nICS_BS ); - - remoteSystem->myExternalSystemAddress = bsSystemAddress; - - // Bug: If A connects to B through R, A's firstExternalID is set to R. If A tries to send to R, sends to loopback because R==firstExternalID - // Correct fix is to specify in Connect() if target is through a proxy. - // However, in practice you have to connect to something else first anyway to know about the proxy. So setting once only is good enough - if (firstExternalID==UNASSIGNED_SYSTEM_ADDRESS) - { - firstExternalID=bsSystemAddress; - firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); - } - - // Hold this until the session handshake completes; the bytes are copied because `data` - // is freed as soon as this branch returns. - ClearSessionConfig(remoteSystem); - remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); - remoteSystem->withheldConnectionPacketData = (unsigned char*) rakMalloc_Ex(byteSize, _FILE_AND_LINE_); - if (remoteSystem->withheldConnectionPacketData!=0) - { - memcpy(remoteSystem->withheldConnectionPacketData, data, byteSize); - remoteSystem->withheldConnectionPacketLength = byteSize; - } - // AllocPacket used to take ownership of `data` here; the copy above means this branch does. - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else - { - // Send to game even if already connected. This could happen when connecting to 127.0.0.1 - // Ignore, already connected - // rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else if ( (unsigned char) data[ 0 ] == ID_CONNECTED_PONG && byteSize == sizeof(unsigned char)+sizeof(MafiaNet::Time)*2 ) - { - MafiaNet::Time sendPingTime, sendPongTime; - - // Copy into the ping times array the current time - the value returned - // First extract the sent ping - MafiaNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); - //PingStruct ps; - //ps.Deserialize(psBS); - inBitStream.IgnoreBits(8); - inBitStream.Read(sendPingTime); - inBitStream.Read(sendPongTime); - - OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); - - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char)data[0] == ID_CONNECTED_PING && byteSize == sizeof(unsigned char)+sizeof(MafiaNet::Time) ) - { - MafiaNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); - inBitStream.IgnoreBits(8); - MafiaNet::Time sendPingTime; - inBitStream.Read(sendPingTime); - - MafiaNet::BitStream outBitStream; - outBitStream.Write((MessageID)ID_CONNECTED_PONG); - outBitStream.Write(sendPingTime); - outBitStream.Write(MafiaNet::GetTime()); - SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Unreliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0 ); - - // Update again immediately after this tick so the ping goes out right away - quitAndDataEvents.SetEvent(); - - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char) data[ 0 ] == ID_DISCONNECTION_NOTIFICATION ) - { - // Stash any reason payload (everything after the 1-byte ID) so it can ride along with the - // user-facing notification packet synthesized once outstanding ACKs are flushed. This raw - // reliability-layer buffer is freed below, so the bytes have to be copied out now. - ClearDisconnectReason(remoteSystem); - if (byteSize > sizeof(unsigned char)) - { - const unsigned int reasonLength = byteSize - (unsigned int) sizeof(unsigned char); - remoteSystem->disconnectReasonData = (unsigned char*) rakMalloc_Ex(reasonLength, _FILE_AND_LINE_); - if (remoteSystem->disconnectReasonData != 0) - { - memcpy(remoteSystem->disconnectReasonData, data + sizeof(unsigned char), reasonLength); - remoteSystem->disconnectReasonLength = reasonLength; - } - } - - // We shouldn't close the connection immediately because we need to ack the ID_DISCONNECTION_NOTIFICATION - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ON_NO_ACK; - rakFree_Ex(data, _FILE_AND_LINE_ ); - - // AddPacketToProducer(packet); - } - else if ( (unsigned char)(data)[0] == ID_DETECT_LOST_CONNECTIONS && byteSize == sizeof(unsigned char) ) - { - // Do nothing - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG_REQUEST ) - { - // Server side of the session handshake. The client's payload arrives before we answer, so a - // server can inspect it and refuse without ever having disclosed its own. - if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigAwaitingLocalDecision==false) - { - if (StoreRemoteSessionConfig(remoteSystem, (const unsigned char*)data + sizeof(MessageID), byteSize - (unsigned int)sizeof(MessageID))==false) - { - // Oversized payload is a protocol violation; drop without answering. - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if (sessionConfigInteractive) - { - // Let the application decide. The connection stays unreported until it calls - // AcceptSession() or RejectSession(), or the handshake times out below. - remoteSystem->sessionConfigAwaitingLocalDecision=true; - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - } - else - { - rakPeerMutexes[ sessionConfig_Mutex ].Lock(); - SendSessionConfigResponse(remoteSystem, (const char*)sessionConfig.GetData(), (unsigned int)sessionConfig.GetNumberOfBytesUsed()); - rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else - { - // A repeat request, or one on an already-established connection: ignore it rather than - // letting a peer re-run the handshake and re-produce the connection packet. - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG ) - { - // Client side of the session handshake: the server's payload is the last thing we needed, so - // the withheld ID_CONNECTION_REQUEST_ACCEPTED goes out now and the connection becomes real. - // Only the peer that initiated the connection may be answered this way, so a client cannot - // push a payload at a server by replying with the server's own message. - if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigIsConnectingSide) - { - if (StoreRemoteSessionConfig(remoteSystem, (const unsigned char*)data + sizeof(MessageID), byteSize - (unsigned int)sizeof(MessageID))==false) - { - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - } - else - { - remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; - remoteSystem->sessionConfigStartTime=0; - ProduceWithheldConnectionPacket(remoteSystem, (MessageID)ID_CONNECTION_REQUEST_ACCEPTED); - } - } - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG_STATUS ) - { - // Client side, while the server's application decides: surface the status and restart our own - // session timer, which is what lets a queued client wait longer than the timeout. Bound to the - // connecting side like the other replies, so a client cannot push one at a server. - if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigIsConnectingSide) - { - remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - } - else - { - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG_REJECTED ) - { - // Only meaningful to the peer that initiated the connection. Without this a malicious client - // could inject ID_CONNECTION_ATTEMPT_FAILED -- a message the application only ever expects for - // its own outbound connects -- into a listening server's queue. - if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigIsConnectingSide) - { - // Rewrite in place so the reason string the server supplied stays at data+1, matching the - // ID_DISCONNECTION_NOTIFICATION reason convention. No connection is ever reported. - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->data[0]=(unsigned char)ID_CONNECTION_ATTEMPT_FAILED; - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - } - else - { - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else if ( (unsigned char)(data)[0] == ID_INVALID_PASSWORD ) - { - if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) - { - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - AddPacketToProducer(packet); - - remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; - } - else - { - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST_ACCEPTED ) - { - if (byteSize > sizeof(MessageID)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(SystemIndex)+sizeof(MafiaNet::Time)*2) - { - // Make sure this connection accept is from someone we wanted to connect to - bool allowConnection, alreadyConnected; - - if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || - remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || - allowConnectionResponseIPMigration) - allowConnection=true; - else - allowConnection=false; - - if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) - alreadyConnected=true; - else - alreadyConnected=false; - - if ( allowConnection ) - { - SystemAddress externalID; - SystemIndex systemIndex; -// SystemAddress internalID; - - MafiaNet::BitStream inBitStream((unsigned char *) data, byteSize, false); - inBitStream.IgnoreBits(8); - // inBitStream.Read(remotePort); - inBitStream.Read(externalID); - inBitStream.Read(systemIndex); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); - - MafiaNet::Time sendPingTime, sendPongTime; - inBitStream.Read(sendPingTime); - inBitStream.Read(sendPongTime); - OnConnectedPong(sendPingTime, sendPongTime, remoteSystem); - - // Find a free remote system struct to use - // MafiaNet::BitStream casBitS(data, byteSize, false); - // ConnectionAcceptStruct cas; - // cas.Deserialize(casBitS); - // systemAddress.GetPort() = remotePort; - - // The remote system told us our external IP, so save it - remoteSystem->myExternalSystemAddress = externalID; - // Transport is up, but the application is not told so until the session payloads have been - // exchanged. ID_CONNECTION_REQUEST_ACCEPTED is stashed and replayed from ID_SESSION_CONFIG. - remoteSystem->connectMode=RemoteSystemStruct::EXCHANGING_SESSION_DATA; - remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); - - // Bug: If A connects to B through R, A's firstExternalID is set to R. If A tries to send to R, sends to loopback because R==firstExternalID - // Correct fix is to specify in Connect() if target is through a proxy. - // However, in practice you have to connect to something else first anyway to know about the proxy. So setting once only is good enough - if (firstExternalID==UNASSIGNED_SYSTEM_ADDRESS) - { - firstExternalID=externalID; - firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); - } - - // Hold the connection-complete packet until the session handshake finishes. The bytes are - // copied because `data` is freed as soon as this branch returns. - ClearSessionConfig(remoteSystem); - remoteSystem->sessionConfigIsConnectingSide=true; - remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); - remoteSystem->withheldConnectionPacketData = (unsigned char*) rakMalloc_Ex(byteSize, _FILE_AND_LINE_); - if (remoteSystem->withheldConnectionPacketData!=0) - { - memcpy(remoteSystem->withheldConnectionPacketData, data, byteSize); - remoteSystem->withheldConnectionPacketLength = byteSize; - } - // AllocPacket used to take ownership of `data` here; the copy above means this branch does. - rakFree_Ex(data, _FILE_AND_LINE_ ); - - MafiaNet::BitStream outBitStream; - outBitStream.Write((MessageID)ID_NEW_INCOMING_CONNECTION); - outBitStream.Write(systemAddress); - for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) - outBitStream.Write(ipList[i]); - outBitStream.Write(sendPongTime); - outBitStream.Write(MafiaNet::GetTime()); - - SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0 ); - - // Our half of the session handshake. Sent on the same ordered channel right behind - // ID_NEW_INCOMING_CONNECTION, so the server has the transport connection before this lands. - { - MafiaNet::BitStream sessionBitStream; - sessionBitStream.Write((MessageID)ID_SESSION_CONFIG_REQUEST); - rakPeerMutexes[ sessionConfig_Mutex ].Lock(); - if (sessionConfig.GetNumberOfBytesUsed() > 0) - sessionBitStream.Write((const char*)sessionConfig.GetData(), sessionConfig.GetNumberOfBytesUsed()); - rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); - SendImmediate( (char*)sessionBitStream.GetData(), sessionBitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0 ); - } - - if (alreadyConnected==false) - { - PingInternal( systemAddress, true, MafiaNet::Reliability::Unreliable ); - } - } - else - { - // Ignore, already connected - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else - { - // Version mismatch error? - RakAssert(0); - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - else - { - // What do I do if I get a message from a system, before I am fully connected? - // I can either ignore it or give it to the user - // It seems like giving it to the user is a better option - // - // Except while the session handshake is running: the application has not been told this - // connection exists, so handing it application data from that peer would deliver a packet - // for a guid it has never seen connect -- and from a peer it may still refuse. A peer that - // respects the protocol cannot send here anyway: its own public send path is gated on the - // same state, so anything arriving in this window is malformed or hostile. - if ((data[0]>=(MessageID)ID_TIMESTAMP || data[0]==ID_SND_RECEIPT_ACKED || data[0]==ID_SND_RECEIPT_LOSS) && - remoteSystem->isActive && - remoteSystem->connectMode!=RemoteSystemStruct::EXCHANGING_SESSION_DATA - ) - { - packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); - packet->bitSize = bitSize; - packet->systemAddress = systemAddress; - packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; - packet->guid = remoteSystem->guid; - packet->guid.systemIndex=packet->systemAddress.systemIndex; - if (!DispatchNetworkThreadReceive(packet)) - AddPacketToProducer(packet); - } - else - { - rakFree_Ex(data, _FILE_AND_LINE_ ); - } - } - } - - // Does the reliability layer have any more packets waiting for us? - // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer - bitSize = remoteSystem->reliabilityLayer.Receive( &data ); - } - - } - - return true; -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -void RakPeer::OnRNS2Recv(RNS2RecvStruct *recvStruct) -{ - if (incomingDatagramEventHandler) - { - if (incomingDatagramEventHandler(recvStruct)!=true) - return; - } - - PushBufferedPacket(recvStruct); - quitAndDataEvents.SetEvent(); -} - -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -RAK_THREAD_DECLARATION(MafiaNet::UpdateNetworkLoop) -{ - - - - RakPeer * rakPeer = ( RakPeer * ) arguments; - - -/* - // 11/15/05 - this is slower than Sleep() -#ifdef _WIN32 -#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) - // Lets see if these timers give better performance than Sleep - HANDLE timerHandle; - LARGE_INTEGER dueTime; - - if ( rakPeer->threadSleepTimer <= 0 ) - rakPeer->threadSleepTimer = 1; - - // 2nd parameter of false means synchronization timer instead of manual-reset timer - timerHandle = CreateWaitableTimer( nullptr, FALSE, 0 ); - - RakAssert( timerHandle ); - - dueTime.QuadPart = -10000 * rakPeer->threadSleepTimer; // 10000 is 1 ms? - - BOOL success = SetWaitableTimer( timerHandle, &dueTime, rakPeer->threadSleepTimer, nullptr, nullptr, FALSE ); - (void) success; - RakAssert( success ); - -#endif -#endif -*/ - - BitStream updateBitStream( MAXIMUM_MTU_SIZE -#if LIBCAT_SECURITY==1 - + cat::AuthenticatedEncryption::OVERHEAD_BYTES -#endif - ); -// - rakPeer->isMainLoopThreadActive = true; - - bool running = true; - while ( running ) - { - if (rakPeer->endThreads) { - // allow just one more final update-run prior to shutting down this thread to make sure that outstanding acks are still sent and the peers don't unnecessary wait for already retrieved packets - // note: this fixes part of SLNET-123 - running = false; - } -// #ifdef _DEBUG -// // Sanity check, make sure RunUpdateCycle does not block or not otherwise get called for a long time -// RakNetTime thisCall=MafiaNet::GetTime(); -// RakAssert(thisCall-lastCall<250); -// lastCall=thisCall; -// #endif - if (rakPeer->userUpdateThreadPtr) - rakPeer->userUpdateThreadPtr(rakPeer, rakPeer->userUpdateThreadData); - - rakPeer->RunUpdateCycle(updateBitStream); - - // Pending sends go out this often, unless quitAndDataEvents is set - rakPeer->quitAndDataEvents.WaitOnEvent(10); - - /* - -// #if ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && -#if defined(USE_WAIT_FOR_MULTIPLE_EVENTS) && defined(_WIN32) - - if (rakPeer->threadSleepTimer>0) - { - WSAEVENT eventArray[256]; - unsigned int i, eventArrayIndex; - for (i=0,eventArrayIndex=0; i < rakPeer->socketList.Size(); i++) - { - if (rakPeer->socketList[i]->recvEvent!=INVALID_HANDLE_VALUE) - { - eventArray[eventArrayIndex]=rakPeer->socketList[i]->recvEvent; - eventArrayIndex++; - if (eventArrayIndex==256) - break; - } - } - WSAWaitForMultipleEvents(eventArrayIndex,(const HANDLE*) &eventArray,FALSE,rakPeer->threadSleepTimer,FALSE); - } - else - { - RakSleep(0); - } - -#else // ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) - #pragma message("-- RakNet: Using Sleep(). Uncomment USE_WAIT_FOR_MULTIPLE_EVENTS in defines.h if you want to use WaitForSingleObject instead. --") - - RakSleep( rakPeer->threadSleepTimer ); -#endif - */ - } - - rakPeer->isMainLoopThreadActive = false; - - /* -#ifdef _WIN32 -#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) - CloseHandle(timerHandle); -#endif -#endif - */ - - - - - return 0; - -} - -void RakPeer::CallPluginCallbacks(DataStructures::List &pluginList, Packet *packet) -{ - for (unsigned int i=0; i < pluginList.Size(); i++) - { - switch (packet->data[0]) - { - case ID_DISCONNECTION_NOTIFICATION: - pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_DISCONNECTION_NOTIFICATION); - break; - case ID_CONNECTION_LOST: - pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_CONNECTION_LOST); - break; - case ID_NEW_INCOMING_CONNECTION: - pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, true); - break; - case ID_CONNECTION_REQUEST_ACCEPTED: - pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, false); - break; - case ID_CONNECTION_ATTEMPT_FAILED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_ATTEMPT_FAILED); - break; - case ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); - break; - case ID_OUR_SYSTEM_REQUIRES_SECURITY: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_OUR_SYSTEM_REQUIRES_SECURITY); - break; - case ID_PUBLIC_KEY_MISMATCH: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_PUBLIC_KEY_MISMATCH); - break; - case ID_ALREADY_CONNECTED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_ALREADY_CONNECTED); - break; - case ID_NO_FREE_INCOMING_CONNECTIONS: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_NO_FREE_INCOMING_CONNECTIONS); - break; - case ID_CONNECTION_BANNED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_BANNED); - break; - case ID_INVALID_PASSWORD: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INVALID_PASSWORD); - break; - case ID_INCOMPATIBLE_PROTOCOL_VERSION: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INCOMPATIBLE_PROTOCOL); - break; - case ID_IP_RECENTLY_CONNECTED: - pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_IP_RECENTLY_CONNECTED); - break; - } - } -} - -void RakPeer::FillIPList(void) -{ - if (ipList[0]!=UNASSIGNED_SYSTEM_ADDRESS) - return; - - // Fill out ipList structure - RakNetSocket2::GetMyIP( ipList ); - - // Sort the addresses from lowest to highest - int startingIdx = 0; - while (startingIdx < MAXIMUM_NUMBER_OF_INTERNAL_IDS-1 && ipList[startingIdx] != UNASSIGNED_SYSTEM_ADDRESS) - { - int lowestIdx = startingIdx; - for (int curIdx = startingIdx + 1; curIdx < MAXIMUM_NUMBER_OF_INTERNAL_IDS-1 && ipList[curIdx] != UNASSIGNED_SYSTEM_ADDRESS; curIdx++ ) - { - if (ipList[curIdx] < ipList[startingIdx]) - { - lowestIdx = curIdx; - } - } - if (startingIdx != lowestIdx) - { - SystemAddress temp = ipList[startingIdx]; - ipList[startingIdx] = ipList[lowestIdx]; - ipList[lowestIdx] = temp; - } - ++startingIdx; - } -} - - -// #if defined(RMO_NEW_UNDEF_ALLOCATING_QUEUE) -// #pragma pop_macro("new") -// #undef RMO_NEW_UNDEF_ALLOCATING_QUEUE -// #endif - +/* + * Original work: Copyright (c) 2014, Oculus VR, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * RakNet License.txt file in the licenses directory of this source tree. An additional grant + * of patent rights can be found in the RakNet Patents.txt file in the same directory. + * + * + * Modified work: Copyright (c) 2016-2020, SLikeSoft UG (haftungsbeschränkt) + * + * This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style + * license found in the license.txt file in the root directory of this source tree. + */ + +// \file +// + + + +#define CAT_NEUTER_EXPORT /* Neuter dllimport for libcat */ + +#include "mafianet/defines.h" +#include "mafianet/peer.h" +#include "mafianet/session_admission.h" +#include "mafianet/types.h" + +#ifdef _WIN32 + +#else +#include +#endif + +// #if defined(new) +// #pragma push_macro("new") +// #undef new +// #define RMO_NEW_UNDEF_ALLOCATING_QUEUE +// #endif + +#include +#include // toupper +#include +#include "mafianet/get_time.h" +#include +#include +#include "mafianet/message_identifiers.h" +#include "mafianet/mtu_black_hole.h" +#include "mafianet/ds_huffman_encoding_tree.h" +#include "mafianet/rand.h" +#include "mafianet/plugin_interface2.h" +#include "mafianet/string_compressor.h" +#include "mafianet/string_table.h" +#include "mafianet/network_id_object.h" +#include "mafianet/types.h" +#include "mafianet/dr_sha1.h" +#include "mafianet/sleep.h" +#include "mafianet/assert.h" +#include "mafianet/version.h" +#include "mafianet/network_id_manager.h" +#include "mafianet/get_time_of_day.h" +#include "mafianet/signaled_event.h" +#include "mafianet/super_fast_hash.h" +#include "mafianet/alloca.h" +#include "mafianet/wsa_startup_singleton.h" +#include "mafianet/linux_adapter.h" +#include "mafianet/osx_adapter.h" + +#ifdef USE_THREADED_SEND +#include "mafianet/send_to_thread.h" +#endif + +#ifdef CAT_AUDIT +#define CAT_AUDIT_PRINTF(...) printf(__VA_ARGS__) +#else +#define CAT_AUDIT_PRINTF(...) +#endif + +namespace MafiaNet +{ +RAK_THREAD_DECLARATION(UpdateNetworkLoop); +RAK_THREAD_DECLARATION(RecvFromLoop); +RAK_THREAD_DECLARATION(UDTConnect); +} +#define REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE 8 + +#if !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ ) +#include // malloc +#endif + + + +#if defined(_WIN32) +// +#else +/* +#include // Console 2 +#include +extern bool _extern_Console2LoadModules(void); +extern int _extern_Console2GetConnectionStatus(void); +extern int _extern_Console2GetLobbyStatus(void); +//extern bool Console2StartupFluff(unsigned int *); +extern void Console2ShutdownFluff(void); +//extern unsigned int Console2ActivateConnection(unsigned int, void *); +//extern bool Console2BlockOnEstablished(void); +extern void Console2GetIPAndPort(unsigned int, char *, unsigned short *, unsigned int ); +//extern void Console2DeactivateConnection(unsigned int, unsigned int); +*/ +#endif + + +// Probed high to low while connecting: the connecting peer pads +// ID_OPEN_CONNECTION_REQUEST_1 to a rung and steps down when nothing comes back, +// so the negotiated MTU is the largest rung that survived the path. Each rung is +// a whole IP datagram, MAXIMUM_MTU_SIZE first. +// +// 1280 is both the IPv6 minimum MTU and what WireGuard-derived tunnels commonly +// settle on, which makes it the single most valuable intermediate rung; 1024 +// covers heavier or stacked encapsulation; 576 is the dial-up floor. The gap +// from MAXIMUM_MTU_SIZE straight to 1200 that used to sit here meant a peer one +// byte over the top rung gave up ~20% of its payload capacity to find that out. +// +// The ladder itself lives in mtu_black_hole.h: the in-session black-hole +// step-down (ReliabilityLayer) walks the same rungs this handshake probes. +static const int NUM_MTU_SIZES=MafiaNet::MTU_LADDER_SIZE; +static const int *const mtuSizes=MafiaNet::MTU_LADDER; + +// How many connection attempts are spent on each rung of mtuSizes before +// stepping down. +// +// Never zero. Connect() takes sendConnectionAttemptCount from the caller and +// documents no lower bound, so a caller asking for fewer attempts than there are +// rungs used to divide by zero here -- a crash that was unreachable only because +// the default (12) happened to exceed NUM_MTU_SIZES. Such a caller now gets one +// attempt per rung and stops when its budget runs out, having tried the largest +// sizes first. +static unsigned GetConnectionAttemptsPerMTU(unsigned sendConnectionAttemptCount) +{ + const unsigned attemptsPerMTU = sendConnectionAttemptCount / (unsigned) NUM_MTU_SIZES; + return attemptsPerMTU > 0 ? attemptsPerMTU : 1; +} + + +// Note to self - if I change this it might affect RECIPIENT_OFFLINE_MESSAGE_INTERVAL in Natpunchthrough.cpp +//static const int MAX_OPEN_CONNECTION_REQUESTS=8; +//static const int TIME_BETWEEN_OPEN_CONNECTION_REQUESTS=500; + +using namespace MafiaNet; + +static RakNetRandom rnr; + +/* +struct RakPeerAndIndex +{ + RakNetSocket2 *s; + RakPeer *rakPeer; +}; +*/ + +static const unsigned int MAX_OFFLINE_DATA_LENGTH=400; // I set this because I limit ID_CONNECTION_REQUEST to 512 bytes, and the password is appended to that packet. + +// Used to distinguish between offline messages with data, and messages from the reliability layer +// Should be different than any message that could result from messages from the reliability layer +// Make sure highest bit is 0, so isValid in DatagramHeaderFormat is false +static const unsigned char OFFLINE_MESSAGE_DATA_ID[16]={0x00,0xFF,0xFF,0x00,0xFE,0xFE,0xFE,0xFE,0xFD,0xFD,0xFD,0xFD,0x12,0x34,0x56,0x78}; + +struct PacketFollowedByData +{ + Packet p; + unsigned char data[1]; +}; + +Packet *RakPeer::AllocPacket(unsigned dataSize, const char *file, unsigned int line) +{ + // Crashes when dataSize is 4 bytes - not sure why +// unsigned char *data = (unsigned char *) rakMalloc_Ex(sizeof(PacketFollowedByData)+dataSize, file, line); +// Packet *p = &((PacketFollowedByData *)data)->p; +// p->data=((PacketFollowedByData *)data)->data; +// p->length=dataSize; +// p->bitSize=BYTES_TO_BITS(dataSize); +// p->deleteData=false; +// p->guid=UNASSIGNED_RAKNET_GUID; +// return p; + + MafiaNet::Packet *p; + packetAllocationPoolMutex.Lock(); + p = packetAllocationPool.Allocate(file,line); + ++packetsOutstanding; + packetAllocationPoolMutex.Unlock(); + p = new ((void*)p) Packet; + p->data=(unsigned char*) rakMalloc_Ex(dataSize,file,line); + p->length=dataSize; + p->bitSize=BYTES_TO_BITS(dataSize); + p->deleteData=true; + p->guid=UNASSIGNED_RAKNET_GUID; + p->wasGeneratedLocally=false; + return p; +} + +Packet *RakPeer::AllocPacket(unsigned dataSize, unsigned char *data, const char *file, unsigned int line) +{ + // Packet *p = (Packet *)rakMalloc_Ex(sizeof(Packet), file, line); + MafiaNet::Packet *p; + packetAllocationPoolMutex.Lock(); + p = packetAllocationPool.Allocate(file,line); + ++packetsOutstanding; + packetAllocationPoolMutex.Unlock(); + p = new ((void*)p) Packet; + RakAssert(p); + p->data=data; + p->length=dataSize; + p->bitSize=BYTES_TO_BITS(dataSize); + p->deleteData=true; + p->guid=UNASSIGNED_RAKNET_GUID; + p->wasGeneratedLocally=false; + return p; +} + +STATIC_FACTORY_DEFINITIONS(RakPeerInterface,RakPeer) + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Constructor +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RakPeer() +{ +#if LIBCAT_SECURITY==1 + // Encryption and security + CAT_AUDIT_PRINTF("AUDIT: Initializing RakPeer security flags: using_security = false, server_handshake = null, cookie_jar = null\n"); + _using_security = false; + _server_handshake = 0; + _cookie_jar = 0; +#endif + + StringCompressor::AddReference(); + MafiaNet::StringTable::AddReference(); + WSAStartupSingleton::AddRef(); + + defaultMTUSize = mtuSizes[NUM_MTU_SIZES-1]; + trackFrequencyTable = false; + maximumIncomingConnections = 0; + maximumNumberOfPeers = 0; + //remoteSystemListSize=0; + remoteSystemList = 0; + activeSystemList = 0; + activeSystemListSize=0; + remoteSystemLookup=0; + bytesSentPerSecond = bytesReceivedPerSecond = 0; + endThreads = true; + networkThreadReceiveStopped = false; + isMainLoopThreadActive = false; + socketFactory = nullptr; + manualUpdate = false; + incomingDatagramEventHandler=0; + sessionConfigInteractive=false; + maximumPendingSessions=0; + maximumPendingSessionsPerAddress=0; + sessionTimeout=0; + + + + + + // isRecvfromThreadActive=false; +#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0 + occasionalPing = true; +#else + occasionalPing = false; +#endif + allowInternalRouting=false; + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + ipList[i]=UNASSIGNED_SYSTEM_ADDRESS; + allowConnectionResponseIPMigration = false; + //incomingPasswordLength=outgoingPasswordLength=0; + incomingPasswordLength=0; + splitMessageProgressInterval=0; + //unreliableTimeout=0; + unreliableTimeout=1000; + maxOutgoingBPS=0; + firstExternalID=UNASSIGNED_SYSTEM_ADDRESS; + myGuid=UNASSIGNED_RAKNET_GUID; + userUpdateThreadPtr=0; + userUpdateThreadData=0; + +#ifdef _DEBUG + // Wait longer to disconnect in debug so I don't get disconnected while tracing + defaultTimeoutTime=30000; +#else + defaultTimeoutTime=10000; +#endif + +#ifdef _DEBUG + _packetloss=0.0; + _minExtraPing=0; + _extraPingVariance=0; +#endif + + bufferedCommands.SetPageSize(sizeof(BufferedCommandStruct)*16); + socketQueryOutput.SetPageSize(sizeof(SocketQueryOutput)*8); + + packetAllocationPoolMutex.Lock(); + packetAllocationPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); + packetsOutstanding = 0; + packetAllocationPoolMutex.Unlock(); + shutdownGeneration = 0; + + remoteSystemIndexPool.SetPageSize(sizeof(DataStructures::MemoryPool::MemoryWithPage)*32); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GenerateGUID(); + + quitAndDataEvents.InitEvent(); + limitConnectionFrequencyFromTheSameIP=false; + ResetSendReceipt(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Destructor +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::~RakPeer() +{ + Shutdown( 0, 0 ); + + // Free the ban list. + ClearBanList(); + + StringCompressor::RemoveReference(); + MafiaNet::StringTable::RemoveReference(); + WSAStartupSingleton::Deref(); + + quitAndDataEvents.CloseEvent(); + +#if LIBCAT_SECURITY==1 + // Encryption and security + CAT_AUDIT_PRINTF("AUDIT: Deleting RakPeer security objects, handshake = %x, cookie jar = %x\n", _server_handshake, _cookie_jar); + if (_server_handshake) MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + if (_cookie_jar) MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); +#endif + + + + + + + + + + + + + + + + +// for (unsigned int i=0; i < pluginListTS.Size(); i++) +// pluginListTS[i]->SetRakPeerInterface(0); +// for (unsigned int i=0; i < pluginListNTS.Size(); i++) +// pluginListNTS[i]->SetRakPeerInterface(0); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// \brief Starts the network threads, opens the listen port. +// You must call this before calling Connect(). +// Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). +// \note Call SetMaximumIncomingConnections if you want to accept incoming connections +// \param[in] maxConnections The maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.- A hybrid would set it to the sum of both types of connections +// \param[in] localPort The port to listen for connections on. +// \param[in] _threadSleepTimer How many ms to Sleep each internal update cycle. With new congestion control, the best results will be obtained by passing 10. +// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass &socketDescriptor, 1SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); +// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. +// \return False on failure (can't create socket or thread), true on success. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +StartupResult RakPeer::Startup( unsigned int maxConnections, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount, int threadPriority ) +{ + if (IsActive()) + return RAKNET_ALREADY_STARTED; + + SetNetworkThreadReceiveStopped(false); + + // If getting the guid failed in the constructor, try again + if (myGuid.g==0) + { + GenerateGUID(); + if (myGuid.g==0) + return COULD_NOT_GENERATE_GUID; + } + + if (threadPriority==-99999) + { + + +#if defined(_WIN32) + threadPriority=0; + + + + +#else + threadPriority=1000; +#endif + } + + + FillIPList(); + + if (myGuid==UNASSIGNED_RAKNET_GUID) + { + rnr.SeedMT( GenerateSeedFromGuid() ); + } + + //RakPeerAndIndex rpai[32]; + //RakAssert(socketDescriptorCount<32); + + RakAssert(socketDescriptors && socketDescriptorCount>=1); + + if (socketDescriptors==0 || socketDescriptorCount<1) + return INVALID_SOCKET_DESCRIPTORS; + + //unsigned short localPort; + //localPort=socketDescriptors[0].port; + + RakAssert( maxConnections > 0 ); + + if ( maxConnections <= 0 ) + return INVALID_MAX_CONNECTIONS; + + DerefAllSockets(); + + + unsigned i; + // Go through all socket descriptors and precreate sockets on the specified addresses + for (i=0; iCreate(socketDescriptors[i], i) : RakNetSocket2Allocator::AllocRNS2(); + if (r2 == nullptr) + { + DerefAllSockets(); + return SOCKET_PORT_ALREADY_IN_USE; + } + r2->SetUserConnectionSocketIndex(i); + if (r2->IsBerkleySocket()) + { + RNS2_BerkleyBindParameters bbp; + bbp.port=socketDescriptors[i].port; + bbp.hostAddress=(char*) socketDescriptors[i].hostAddress; + bbp.addressFamily=socketDescriptors[i].socketFamily; + bbp.type=SOCK_DGRAM; + bbp.protocol=socketDescriptors[i].extraSocketOptions; + bbp.nonBlockingSocket=false; + bbp.setBroadcast=true; + bbp.setIPHdrIncl=false; + bbp.doNotFragment=false; + bbp.pollingThreadPriority=threadPriority; + bbp.eventHandler=this; + RNS2BindResult br = ((RNS2_Berkley*) r2)->Bind(&bbp, _FILE_AND_LINE_); + + if ( + #if RAKNET_SUPPORT_IPV6==0 + socketDescriptors[i].socketFamily!=AF_INET || + #endif + br==BR_REQUIRES_RAKNET_SUPPORT_IPV6_DEFINE) + { + RakNetSocket2Allocator::DeallocRNS2(r2); + DerefAllSockets(); + return SOCKET_FAMILY_NOT_SUPPORTED; + } + else if (br==BR_FAILED_TO_BIND_SOCKET) + { + RakNetSocket2Allocator::DeallocRNS2(r2); + DerefAllSockets(); + return SOCKET_PORT_ALREADY_IN_USE; + } + else if (br==BR_FAILED_SEND_TEST) + { + RakNetSocket2Allocator::DeallocRNS2(r2); + DerefAllSockets(); + return SOCKET_FAILED_TEST_SEND; + } + else + { + RakAssert(br==BR_SUCCESS); + } + } + else + { + // Factory-supplied: already bound; it delivers datagrams to us. + r2->SetRecvEventHandler(this); + } + + socketList.Push(r2, _FILE_AND_LINE_ ); + + } + + for (i=0; iIsBerkleySocket()) + ((RNS2_Berkley*) socketList[i])->CreateRecvPollingThread(threadPriority); + } + + for (i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + { + if (ipList[i]==UNASSIGNED_SYSTEM_ADDRESS) + break; + // #high - using the 1st socket here is flawed - in cases of having multiple sockets (f.e. different ports and different families (i.e. IPv4/IPv6) we must use the proper + // socket for each IP address in the list + if (socketList[0]->IsBerkleySocket()) + { + unsigned short port = ((RNS2_Berkley*)socketList[0])->GetBoundAddress().GetPort(); + ipList[i].SetPortHostOrder(port); + + } + else + { + ipList[i].SetPortHostOrder(socketList[0]->GetBoundAddress().GetPort()); + } + } + + if ( maximumNumberOfPeers == 0 ) + { + // Don't allow more incoming connections than we have peers. + if ( maximumIncomingConnections > maxConnections ) + maximumIncomingConnections = maxConnections; + + maximumNumberOfPeers = maxConnections; + // 04/19/2006 - Don't overallocate because I'm no longer allowing connected pings. + // The disconnects are not consistently processed and the process was sloppy and complicated. + // Allocate 10% extra to handle new connections from players trying to connect when the server is full + //remoteSystemListSize = maxConnections;// * 11 / 10 + 1; + + // remoteSystemList in Single thread + //remoteSystemList = MafiaNet::OP_NEW( _FILE_AND_LINE_ ); + remoteSystemList = MafiaNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); + + remoteSystemLookup = MafiaNet::OP_NEW_ARRAY((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE, _FILE_AND_LINE_ ); + + activeSystemList = MafiaNet::OP_NEW_ARRAY(maximumNumberOfPeers, _FILE_AND_LINE_ ); + + for ( i = 0; i < maximumNumberOfPeers; i++ ) + //for ( i = 0; i < remoteSystemListSize; i++ ) + { + // remoteSystemList in Single thread + remoteSystemList[ i ].isActive = false; + remoteSystemList[ i ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; + remoteSystemList[ i ].guid = UNASSIGNED_RAKNET_GUID; + remoteSystemList[ i ].myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; + remoteSystemList[ i ].connectMode=RemoteSystemStruct::NO_ACTION; + remoteSystemList[ i ].MTUSize = defaultMTUSize; + remoteSystemList[ i ].remoteSystemIndex = (SystemIndex) i; + // One-time zero-init: the array is OP_NEW_ARRAY allocated with no member init, so prime the + // reason-payload fields here before ClearDisconnectReason() can ever free them. + remoteSystemList[ i ].disconnectReasonData = 0; + remoteSystemList[ i ].disconnectReasonLength = 0; + remoteSystemList[ i ].remoteSessionConfigData = 0; + remoteSystemList[ i ].remoteSessionConfigLength = 0; + remoteSystemList[ i ].withheldConnectionPacketData = 0; + remoteSystemList[ i ].withheldConnectionPacketLength = 0; + remoteSystemList[ i ].sessionConfigAwaitingLocalDecision = false; + remoteSystemList[ i ].sessionConfigIsConnectingSide = false; + remoteSystemList[ i ].connectionReportedToApplication = false; + remoteSystemList[ i ].sessionConfigStartTime = 0; +#ifdef _DEBUG + remoteSystemList[ i ].reliabilityLayer.ApplyNetworkSimulator(_packetloss, _minExtraPing, _extraPingVariance); +#endif + + // All entries in activeSystemList have valid pointers all the time. + activeSystemList[ i ] = &remoteSystemList[ i ]; + } + + for (i=0; i < (unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE; i++) + { + remoteSystemLookup[i]=0; + } + } + + // For histogram statistics + // nextReadBytesTime=0; + // lastSentBytes=lastReceivedBytes=0; + + if ( endThreads ) + { + updateCycleIsRunning = false; + endThreads = false; + firstExternalID=UNASSIGNED_SYSTEM_ADDRESS; + + ClearBufferedCommands(); + ClearBufferedPackets(); + ClearSocketQueryOutput(); + + if ( isMainLoopThreadActive == false && !manualUpdate ) + { +#if RAKPEER_USER_THREADED!=1 + + int errorCode; + + + + + + + + errorCode = MafiaNet::RakThread::Create(UpdateNetworkLoop, this, threadPriority); + + + if ( errorCode != 0 ) + { + Shutdown( 0, 0 ); + return FAILED_TO_CREATE_NETWORK_THREAD; + } +// RakAssert(isRecvFromLoopThreadActive.GetValue()==0); +#endif // RAKPEER_USER_THREADED!=1 + + /* + for (i=0; i(_FILE_AND_LINE_); + rpai->s=socketList[i]; + rpai->rakPeer=this; + +#if RAKPEER_USER_THREADED!=1 + + errorCode = MafiaNet::RakThread::Create(RecvFromLoop, rpai, threadPriority); + + if ( errorCode != 0 ) + { + Shutdown( 0, 0 ); + return FAILED_TO_CREATE_NETWORK_THREAD; + } +#endif // RAKPEER_USER_THREADED!=1 + } + */ + + + /* +#if RAKPEER_USER_THREADED!=1 + + while ( isRecvFromLoopThreadActive.GetValue() < (uint32_t) socketDescriptorCount ) + RakSleep(10); + #endif // RAKPEER_USER_THREADED!=1 + */ + + } + +#if RAKPEER_USER_THREADED!=1 + // Wait for the threads to activate. When they are active they will set these variables to true + while ( !manualUpdate && isMainLoopThreadActive == false ) + RakSleep(10); +#endif // RAKPEER_USER_THREADED!=1 + } + + for (i=0; i < pluginListTS.Size(); i++) + { + pluginListTS[i]->OnRakPeerStartup(); + } + + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginListNTS[i]->OnRakPeerStartup(); + } + +#ifdef USE_THREADED_SEND + MafiaNet::SendToThread::AddRef(); +#endif + + return RAKNET_STARTED; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Must be called while offline +// +// If you accept connections, you must call this or else security will not be enabled for incoming connections. +// +// This feature requires more round trips, bandwidth, and CPU time for the connection handshake +// x64 builds require under 25% of the CPU time of other builds +// +// See the Encryption sample for example usage +// +// Parameters: +// publicKey = A pointer to the public key for accepting new connections +// privateKey = A pointer to the private key for accepting new connections +// If the private keys are 0, then a new key will be generated when this function is called +// bRequireClientKey: Should be set to false for most servers. Allows the server to accept a public key from connecting clients as a proof of identity but eats twice as much CPU time as a normal connection +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::InitializeSecurity(const char *public_key, const char *private_key, bool bRequireClientKey) +{ +#if LIBCAT_SECURITY==1 + if ( endThreads == false ) + return false; + + // Copy client public key requirement flag + _require_client_public_key = bRequireClientKey; + + if (_server_handshake) + { + CAT_AUDIT_PRINTF("AUDIT: Deleting old server_handshake %x\n", _server_handshake); + MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + } + if (_cookie_jar) + { + CAT_AUDIT_PRINTF("AUDIT: Deleting old cookie jar %x\n", _cookie_jar); + MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); + } + + _server_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); + _cookie_jar = MafiaNet::OP_NEW(_FILE_AND_LINE_); + + CAT_AUDIT_PRINTF("AUDIT: Created new server_handshake %x\n", _server_handshake); + CAT_AUDIT_PRINTF("AUDIT: Created new cookie jar %x\n", _cookie_jar); + CAT_AUDIT_PRINTF("AUDIT: Running _server_handshake->Initialize()\n"); + + if (_server_handshake->Initialize(public_key, private_key)) + { + CAT_AUDIT_PRINTF("AUDIT: Successfully initialized, filling cookie jar with goodies, storing public key and setting using security flag to true\n"); + + _server_handshake->FillCookieJar(_cookie_jar); + + memcpy(my_public_key, public_key, sizeof(my_public_key)); + + _using_security = true; + return true; + } + + CAT_AUDIT_PRINTF("AUDIT: Failure to initialize so deleting server handshake and cookie jar; also setting using_security flag = false\n"); + + MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + _server_handshake=0; + MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); + _cookie_jar=0; + _using_security = false; + return false; +#else + (void) public_key; + (void) private_key; + (void) bRequireClientKey; + + return false; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description +// Must be called while offline +// Disables security for incoming connections. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DisableSecurity( void ) +{ +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: DisableSecurity() called, so deleting _server_handshake %x and cookie_jar %x\n", _server_handshake, _cookie_jar); + MafiaNet::OP_DELETE(_server_handshake,_FILE_AND_LINE_); + _server_handshake=0; + MafiaNet::OP_DELETE(_cookie_jar,_FILE_AND_LINE_); + _cookie_jar=0; + + _using_security = false; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AddToSecurityExceptionList(const char *ip) +{ + securityExceptionMutex.Lock(); + securityExceptionList.Insert(RakString(ip), _FILE_AND_LINE_); + securityExceptionMutex.Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RemoveFromSecurityExceptionList(const char *ip) +{ + if (securityExceptionList.Size()==0) + return; + + if (ip==0) + { + securityExceptionMutex.Lock(); + securityExceptionList.Clear(false, _FILE_AND_LINE_); + securityExceptionMutex.Unlock(); + } + else + { + unsigned i=0; + securityExceptionMutex.Lock(); + while (i < securityExceptionList.Size()) + { + if (securityExceptionList[i].IPAddressMatch(ip)) + { + securityExceptionList[i]=securityExceptionList[securityExceptionList.Size()-1]; + securityExceptionList.RemoveAtIndex(securityExceptionList.Size()-1); + } + else + i++; + } + securityExceptionMutex.Unlock(); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsInSecurityExceptionList(const char *ip) +{ + if (securityExceptionList.Size()==0) + return false; + + unsigned i=0; + securityExceptionMutex.Lock(); + for (; i < securityExceptionList.Size(); i++) + { + if (securityExceptionList[i].IPAddressMatch(ip)) + { + securityExceptionMutex.Unlock(); + return true; + } + } + securityExceptionMutex.Unlock(); + return false; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sets how many incoming connections are allowed. If this is less than the number of players currently connected, no +// more players will be allowed to connect. If this is greater than the maximum number of peers allowed, it will be reduced +// to the maximum number of peers allowed. Defaults to 0. +// +// Parameters: +// numberAllowed - Maximum number of incoming connections allowed. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetMaximumIncomingConnections( unsigned short numberAllowed ) +{ + maximumIncomingConnections = numberAllowed; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the maximum number of incoming connections, which is always <= maxConnections +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetMaximumIncomingConnections( void ) const +{ + return maximumIncomingConnections; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns how many open connections there are at this time +// \return the number of open connections +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned short RakPeer::NumberOfConnections(void) const +{ + DataStructures::List addresses; + DataStructures::List guids; + GetSystemList(addresses, guids); + return (unsigned short) addresses.Size(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sets the password incoming connections must match in the call to Connect (defaults to none) +// Pass 0 to passwordData to specify no password +// +// Parameters: +// passwordData: A data block that incoming connections must match. This can be just a password, or can be a stream of data. +// - Specify 0 for no password data +// passwordDataLength: The length in bytes of passwordData +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetIncomingPassword( const char* passwordData, int passwordDataLength ) +{ + //if (passwordDataLength > MAX_OFFLINE_DATA_LENGTH) + // passwordDataLength=MAX_OFFLINE_DATA_LENGTH; + + if (passwordDataLength > 255) + passwordDataLength=255; + + if (passwordData==0) + passwordDataLength=0; + + // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? + // It won't overflow at least because incomingPasswordLength is an unsigned char + if (passwordDataLength>0) + memcpy(incomingPassword, passwordData, passwordDataLength); + incomingPasswordLength=(unsigned char)passwordDataLength; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetIncomingPassword( char* passwordData, int *passwordDataLength ) +{ + if (passwordData==0) + { + *passwordDataLength=incomingPasswordLength; + return; + } + + if (*passwordDataLength > incomingPasswordLength) + *passwordDataLength=incomingPasswordLength; + + if (*passwordDataLength>0) + memcpy(passwordData, incomingPassword, *passwordDataLength); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Call this to connect to the specified host (ip or domain name) and server port. +// Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. Calling both acts as a true peer. +// This is a non-blocking connection. You know the connection is successful when IsConnected() returns true +// or receive gets a packet with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. If the connection is not +// successful, such as rejected connection or no response then neither of these things will happen. +// Requires that you first call Initialize +// +// Parameters: +// host: Either a dotted IP address or a domain name +// remotePort: Which port to connect to on the remote machine. +// passwordData: A data block that must match the data block on the server. This can be just a password, or can be a stream of data +// passwordDataLength: The length in bytes of passwordData +// +// Returns: +// True on successful initiation. False on incorrect parameters, internal error, or too many existing peers +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +ConnectionAttemptResult RakPeer::Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime ) +{ + // If endThreads is true here you didn't call Startup() first. + if ( host == 0 || endThreads || connectionSocketIndex>=socketList.Size() ) + return INVALID_PARAMETER; + + RakAssert(remotePort!=0); + + connectionSocketIndex=GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + + if (passwordDataLength>255) + passwordDataLength=255; + + if (passwordData==0) + passwordDataLength=0; + + // Not threadsafe but it's not important enough to lock. Who is going to change the password a lot during runtime? + // It won't overflow at least because outgoingPasswordLength is an unsigned char +// if (passwordDataLength>0) +// memcpy(outgoingPassword, passwordData, passwordDataLength); +// outgoingPasswordLength=(unsigned char) passwordDataLength; + + // 04/02/09 - Can't remember why I disabled connecting to self, but it seems to work + // Connecting to ourselves in the same instance of the program? +// if ( ( strcmp( host, "127.0.0.1" ) == 0 || strcmp( host, "0.0.0.0" ) == 0 ) && remotePort == mySystemAddress[0].port ) +// return false; + + return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, connectionSocketIndex, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +ConnectionAttemptResult RakPeer::ConnectWithSocket(const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, RakNetSocket2* socket, PublicKey *publicKey, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime) +{ + if ( host == 0 || endThreads || socket == 0 ) + return INVALID_PARAMETER; + + if (passwordDataLength>255) + passwordDataLength=255; + + if (passwordData==0) + passwordDataLength=0; + + return SendConnectionRequest( host, remotePort, passwordData, passwordDataLength, publicKey, 0, 0, sendConnectionAttemptCount, timeBetweenSendConnectionAttemptsMS, timeoutTime, socket ); + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Stops the network threads and close all connections. Multiple calls are ok. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::Shutdown( unsigned int blockDuration, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority ) +{ + unsigned i,j; + bool anyActive; + MafiaNet::TimeMS startWaitingTime; +// SystemAddress systemAddress; + MafiaNet::TimeMS time; + //unsigned short systemListSize = remoteSystemListSize; // This is done for threading reasons + unsigned int systemListSize = maximumNumberOfPeers; + + ++shutdownGeneration; + + if ( blockDuration > 0 ) + { + for ( i = 0; i < systemListSize; i++ ) + { + // remoteSystemList in user thread + if (remoteSystemList[i].isActive) + NotifyAndFlagForShutdown(remoteSystemList[i].systemAddress, false, orderingChannel, disconnectionNotificationPriority); + } + + time = MafiaNet::GetTimeMS(); + startWaitingTime = time; + // Nothing else advances a manually updated peer (or its clock, under a + // test's virtual time): flush the notifications once and stop. + if (manualUpdate) + RunManualUpdate(); + while ( !manualUpdate && time - startWaitingTime < blockDuration ) + { + anyActive=false; + for (j=0; j < systemListSize; j++) + { + // remoteSystemList in user thread + if (remoteSystemList[j].isActive) + { + anyActive=true; + break; + } + } + + // If this system is out of packets to send, then stop waiting + if ( anyActive==false ) + break; + + // This will probably cause the update thread to run which will probably + // send the disconnection notification + + RakSleep(15); + time = MafiaNet::GetTimeMS(); + } + } + // Plugins tear down in OnRakPeerShutdown while the update thread may still be running; stop + // offering them messages there first, so no hook is mid-call or starts after this point. + SetNetworkThreadReceiveStopped(true); + + for (i=0; i < pluginListTS.Size(); i++) + { + pluginListTS[i]->OnRakPeerShutdown(); + } + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginListNTS[i]->OnRakPeerShutdown(); + } + + quitAndDataEvents.SetEvent(); + + endThreads = true; + +// MafiaNet::TimeMS timeout; +#if RAKPEER_USER_THREADED!=1 + + for (i=0; i < socketList.Size(); i++) + { + if (socketList[i]->IsBerkleySocket()) + { + ((RNS2_Berkley *)socketList[i])->SignalStopRecvPollingThread(); + } + } + + /* + // Get recvfrom to unblock + for (i=0; i < socketList.Size(); i++) + { + if (SocketLayer::SendTo(socketList[i], (const char*) &i,1,socketList[i]->GetBoundAddress(), _FILE_AND_LINE_)!=0) + break; + } + */ + + while ( isMainLoopThreadActive ) + { + RakSleep(15); + } + + activeSystemListSize = 0; + + /* + timeout = MafiaNet::GetTimeMS()+1000; + while ( isRecvFromLoopThreadActive.GetValue()>0 && MafiaNet::GetTimeMS()GetBoundAddress(), _FILE_AND_LINE_); + } + + RakSleep(30); + } + */ + + for (i=0; i < socketList.Size(); i++) + { + if (socketList[i]->IsBerkleySocket()) + { + ((RNS2_Berkley *)socketList[i])->BlockOnStopRecvPollingThread(); + } + } + + +#endif // RAKPEER_USER_THREADED!=1 + +// char c=0; +// unsigned int socketIndex; + // remoteSystemList in Single thread + for ( i = 0; i < systemListSize; i++ ) + { + // Reserve this reliability layer for ourselves + remoteSystemList[ i ].isActive = false; + + // Remove any remaining packets + RakAssert(remoteSystemList[ i ].MTUSize <= MAXIMUM_MTU_SIZE); + remoteSystemList[ i ].reliabilityLayer.Reset(false, remoteSystemList[ i ].MTUSize, false); + remoteSystemList[ i ].rakNetSocket = 0; + ClearDisconnectReason(&remoteSystemList[ i ]); + ClearSessionConfig(&remoteSystemList[ i ]); + } + + + // Setting maximumNumberOfPeers to 0 allows remoteSystemList to be reallocated in Initialize. + // Setting remoteSystemListSize prevents threads from accessing the reliability layer + maximumNumberOfPeers = 0; + //remoteSystemListSize = 0; + + // Free any packets the user didn't deallocate + packetReturnMutex.Lock(); + for (i=0; i < packetReturnQueue.Size(); i++) + DeallocatePacket(packetReturnQueue[i]); + packetReturnQueue.Clear(_FILE_AND_LINE_); + packetReturnMutex.Unlock(); + // Packets still out live in the pool's pages: a Shutdown() from inside Receive's dispatch, + // or from code handling a packet Receive returned, must not free the packet under it. The + // pages are reused by the next session and freed by a later Shutdown() or the destructor. + packetAllocationPoolMutex.Lock(); + if (packetsOutstanding == 0) + packetAllocationPool.Clear(_FILE_AND_LINE_); + packetAllocationPoolMutex.Unlock(); + + /* + if (isRecvFromLoopThreadActive.GetValue()>0) + { + timeout = MafiaNet::GetTimeMS()+1000; + while ( isRecvFromLoopThreadActive.GetValue()>0 && MafiaNet::GetTimeMS() addresses; + DataStructures::List guids; + GetSystemList(addresses, guids); + if (remoteSystems) + { + unsigned short i; + for (i=0; i < *numberOfSystems && i < addresses.Size(); i++) + remoteSystems[i]=addresses[i]; + *numberOfSystems=i; + } + else + { + *numberOfSystems=(unsigned short) addresses.Size(); + } + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::GetNextSendReceipt(void) +{ + sendReceiptSerialMutex.Lock(); + uint32_t retVal = sendReceiptSerial; + sendReceiptSerialMutex.Unlock(); + return retVal; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::IncrementNextSendReceipt(void) +{ + sendReceiptSerialMutex.Lock(); + uint32_t returned = sendReceiptSerial; + if (++sendReceiptSerial==0) + sendReceiptSerial=1; + sendReceiptSerialMutex.Unlock(); + return returned; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sends a block of data to the specified system that you are connected to. +// This function only works while the client is connected (Use the Connect function). +// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM +// +// Parameters: +// data: The block of data to send +// length: The size in bytes of the data to send +// bitStream: The bitstream to send +// priority: What priority level to send on. +// reliability: How reliability to send this data +// orderingChannel: When using ordered or sequenced packets, what channel to order these on. +// - Packets are only ordered relative to other packets on the same stream +// systemAddress: Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none +// broadcast: True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. +// Returns: +// \return 0 on bad input. Otherwise a number that identifies this message. If \a reliability is a type that returns a receipt, on a later call to Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS with bytes 1-4 inclusive containing this number +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// A peer mid-session-handshake has not been reported to the application, so the application must not be +// able to address it. Checked here rather than in SendImmediate because the handshake's own messages go +// through SendImmediate directly and must still reach that peer. +bool RakPeer::IsExchangingSessionData( const AddressOrGUID systemIdentifier ) +{ + if (remoteSystemList==0) + return false; + + int index; + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); + else + index = GetIndexFromGuid(systemIdentifier.rakNetGuid); + + return index!=-1 && remoteSystemList[index].isActive && + remoteSystemList[index].connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::Send( const char *data, const int length, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) +{ +#ifdef _DEBUG + RakAssert( data && length > 0 ); +#endif + RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); + RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); + RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); + + if ( data == 0 || length < 0 ) + return 0; + + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + if ( broadcast == false && systemIdentifier.IsUndefined()) + return 0; + + if ( broadcast == false && IsExchangingSessionData(systemIdentifier) ) + return 0; + + uint32_t usedSendReceipt; + if (forceReceiptNumber!=0) + usedSendReceipt=forceReceiptNumber; + else + usedSendReceipt=IncrementNextSendReceipt(); + + if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) + { + SendLoopback(data,length); + + if (reliability>=MafiaNet::Reliability::UnreliableWithAckReceipt) + { + char buff[5]; + buff[0]=ID_SND_RECEIPT_ACKED; + sendReceiptSerialMutex.Lock(); + memcpy(buff+1, &sendReceiptSerial, 4); + sendReceiptSerialMutex.Unlock(); + SendLoopback( buff, 5 ); + } + + return usedSendReceipt; + } + + SendBuffered(data, length*8, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); + + return usedSendReceipt; +} + +void RakPeer::SendLoopback( const char *data, const int length ) +{ + if ( data == 0 || length < 0 ) + return; + + Packet *packet = AllocPacket(length, _FILE_AND_LINE_); + memcpy(packet->data, data, length); + packet->systemAddress = GetLoopbackAddress(); + packet->guid=myGuid; + PushBackPacket(packet, false); +} + +uint32_t RakPeer::Send( const MafiaNet::BitStream * bitStream, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) +{ +#ifdef _DEBUG + RakAssert( bitStream->GetNumberOfBytesUsed() > 0 ); +#endif + + RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); + RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); + RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); + + if ( bitStream->GetNumberOfBytesUsed() == 0 ) + return 0; + + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + if ( broadcast == false && systemIdentifier.IsUndefined() ) + return 0; + + if ( broadcast == false && IsExchangingSessionData(systemIdentifier) ) + return 0; + + uint32_t usedSendReceipt; + if (forceReceiptNumber!=0) + usedSendReceipt=forceReceiptNumber; + else + usedSendReceipt=IncrementNextSendReceipt(); + + if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) + { + SendLoopback((const char*) bitStream->GetData(),bitStream->GetNumberOfBytesUsed()); + if (reliability>=MafiaNet::Reliability::UnreliableWithAckReceipt) + { + char buff[5]; + buff[0]=ID_SND_RECEIPT_ACKED; + sendReceiptSerialMutex.Lock(); + memcpy(buff+1, &sendReceiptSerial,4); + sendReceiptSerialMutex.Unlock(); + SendLoopback( buff, 5 ); + } + return usedSendReceipt; + } + + // Sends need to be buffered and processed in the update thread because the systemAddress associated with the reliability layer can change, + // from that thread, resulting in a send to the wrong player! While I could mutex the systemAddress, that is much slower than doing this + SendBuffered((const char*)bitStream->GetData(), bitStream->GetNumberOfBitsUsed(), priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); + + + return usedSendReceipt; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Sends multiple blocks of data, concatenating them automatically. +// +// This is equivalent to: +// MafiaNet::BitStream bs; +// bs.WriteAlignedBytes(block1, blockLength1); +// bs.WriteAlignedBytes(block2, blockLength2); +// bs.WriteAlignedBytes(block3, blockLength3); +// Send(&bs, ...) +// +// This function only works while connected +// \param[in] data An array of pointers to blocks of data +// \param[in] lengths An array of integers indicating the length of each block of data +// \param[in] numParameters Length of the arrays data and lengths +// \param[in] priority What priority level to send on. See packet_priority.h +// \param[in] reliability How reliability to send this data. See packet_priority.h +// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream +// \param[in] systemIdentifier Who to send this packet to, or in the case of broadcasting who not to send it to. Pass either a SystemAddress structure or a RakNetGUID structure. Use UNASSIGNED_SYSTEM_ADDRESS or to specify none +// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. +// \return False if we are not connected to the specified recipient. True otherwise +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +uint32_t RakPeer::SendList( const char **data, const int *lengths, const int numParameters, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, uint32_t forceReceiptNumber ) +{ +#ifdef _DEBUG + RakAssert( data ); +#endif + + if ( data == 0 || lengths == 0 ) + return 0; + + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + if (numParameters==0) + return 0; + + if (lengths==0) + return 0; + + if ( broadcast == false && systemIdentifier.IsUndefined() ) + return 0; + + if ( broadcast == false && IsExchangingSessionData(systemIdentifier) ) + return 0; + + uint32_t usedSendReceipt; + if (forceReceiptNumber!=0) + usedSendReceipt=forceReceiptNumber; + else + usedSendReceipt=IncrementNextSendReceipt(); + + SendBufferedList(data, lengths, numParameters, priority, reliability, orderingChannel, systemIdentifier, broadcast, RemoteSystemStruct::NO_ACTION, usedSendReceipt); + + return usedSendReceipt; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Gets a packet from the incoming packet queue. Use DeallocatePacket to deallocate the packet after you are done with it. +// Check the Packet struct at the top of CoreNetworkStructures.h for the format of the struct +// +// Returns: +// 0 if no packets are waiting to be handled, otherwise an allocated packet +// If the client is not active this will also return 0, as all waiting packets are flushed when the client is Disconnected +// This also updates all memory blocks associated with synchronized memory and distributed objects +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +Packet* RakPeer::Receive( void ) +{ + if ( !( IsActive() ) ) + return 0; + + MafiaNet::Packet *packet; +// Packet **threadPacket; + PluginReceiveResult pluginResult; + + int offset; + unsigned int i; + + // User should call RunUpdateCycle and RunRecvFromOnce to do this commented code + /* +#if RAKPEER_SINGLE_THREADED==1 + RakPeer::RecvFromStruct *recvFromStruct; + for (i=0; i < socketList.Size(); i++) + { + for(;;) + { + recvFromStruct=bufferedPackets.Allocate( _FILE_AND_LINE_ ); + recvFromStruct->s=socketList[i]->s; + recvFromStruct->extraSocketOptions=socketList[i]->extraSocketOptions; + SocketLayer::RecvFromBlocking( + recvFromStruct->s, this, recvFromStruct->extraSocketOptions, recvFromStruct->data, &recvFromStruct->bytesRead, &recvFromStruct->systemAddress, &recvFromStruct->timeRead); + if (recvFromStruct->bytesRead<=0) + { + bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); + break; + } + else + { + RakAssert(recvFromStruct->systemAddress.GetPort()); + bufferedPackets.Push(recvFromStruct); + } + } + } + + BitStream updateBitStream( MAXIMUM_MTU_SIZE +#if LIBCAT_SECURITY==1 + + cat::AuthenticatedEncryption::OVERHEAD_BYTES +#endif + ); + RunUpdateCycle(0, 0, updateBitStream); +#endif + */ + + for (i=0; i < pluginListTS.Size(); i++) + { + pluginListTS[i]->Update(); + } + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginListNTS[i]->Update(); + } + + do + { + packetReturnMutex.Lock(); + if (packetReturnQueue.IsEmpty()) + packet=0; + else + packet = packetReturnQueue.Pop(); + packetReturnMutex.Unlock(); + if (packet==0) + return 0; + +// unsigned char msgId; + if ( ( packet->length >= sizeof(unsigned char) + sizeof(MafiaNet::Time ) ) && + ( (unsigned char) packet->data[ 0 ] == ID_TIMESTAMP ) ) + { + offset = sizeof(unsigned char); + ShiftIncomingTimestamp( packet->data + offset, packet->systemAddress ); +// msgId=packet->data[sizeof(unsigned char) + sizeof( MafiaNet::Time )]; + } +// else + // msgId=packet->data[0]; + + // Some locally generated packets need to be processed by plugins, for example ID_FCM2_NEW_HOST + // The plugin itself should intercept these messages generated remotely +// if (packet->wasGeneratedLocally) +// return packet; + + + // A callback may shut the peer down. The plugins have been reset by then and the packet + // belongs to a closed session, so it goes no further and Receive returns nothing. + const unsigned int generation = shutdownGeneration; + + CallPluginCallbacks(pluginListTS, packet); + CallPluginCallbacks(pluginListNTS, packet); + if (shutdownGeneration != generation) + { + DeallocatePacket( packet ); + return 0; + } + + for (i=0; i < pluginListTS.Size(); i++) + { + pluginResult=pluginListTS[i]->OnReceive(packet); + if (shutdownGeneration != generation) + { + if (pluginResult!=RR_STOP_PROCESSING) + DeallocatePacket( packet ); + return 0; + } + if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) + { + DeallocatePacket( packet ); + packet=0; // Will do the loop again and get another packet + break; // break out of the enclosing for + } + else if (pluginResult==RR_STOP_PROCESSING) + { + packet=0; + break; + } + } + + for (i=0; i < pluginListNTS.Size(); i++) + { + pluginResult=pluginListNTS[i]->OnReceive(packet); + if (shutdownGeneration != generation) + { + if (pluginResult!=RR_STOP_PROCESSING) + DeallocatePacket( packet ); + return 0; + } + if (pluginResult==RR_STOP_PROCESSING_AND_DEALLOCATE) + { + DeallocatePacket( packet ); + packet=0; // Will do the loop again and get another packet + break; // break out of the enclosing for + } + else if (pluginResult==RR_STOP_PROCESSING) + { + packet=0; + break; + } + } + + } while(packet==0); + +#ifdef _DEBUG + RakAssert( packet->data ); +#endif + + return packet; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Call this to deallocate a packet returned by Receive +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DeallocatePacket( Packet *packet ) +{ + if ( packet == 0 ) + return; + + if (packet->deleteData) + { + rakFree_Ex(packet->data, _FILE_AND_LINE_ ); + packet->~Packet(); + packetAllocationPoolMutex.Lock(); + packetAllocationPool.Release(packet,_FILE_AND_LINE_); + --packetsOutstanding; + packetAllocationPoolMutex.Unlock(); + } + else + { + rakFree_Ex(packet, _FILE_AND_LINE_ ); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Return the total number of connections we are allowed +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetMaximumNumberOfPeers( void ) const +{ + return maximumNumberOfPeers; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). +// +// Parameters: +// target: Which connection to close +// sendDisconnectionNotification: True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. +// channel: If blockDuration > 0, the disconnect packet will be sent on this channel +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::CloseConnection( const AddressOrGUID target, bool sendDisconnectionNotification, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority, const MafiaNet::BitStream *reasonData ) +{ + /* + // This only be called from the user thread, for the user shutting down. + // From the network thread, this should occur because of ID_DISCONNECTION_NOTIFICATION and ID_CONNECTION_LOST + unsigned j; + for (j=0; j < messageHandlerList.Size(); j++) + { + messageHandlerList[j]->OnClosedConnection( + target.systemAddress==UNASSIGNED_SYSTEM_ADDRESS ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress, + target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid, + LCR_CLOSED_BY_USER); + } + */ + + const SystemAddress address = (target.systemAddress == UNASSIGNED_SYSTEM_ADDRESS) ? GetSystemAddressFromGuid(target.rakNetGuid) : target.systemAddress; + int remoteSystemListIndex = GetIndexFromSystemAddress(address); + + // Resolve the socket to close on WITHOUT assuming a valid slot index. + // GetIndexFromSystemAddress returns -1 when the target isn't in the list; never + // coerce that to 0 — reading remoteSystemList[0] would crash if the list is + // unallocated, or target an unrelated peer's slot. When the index is valid use + // that slot's socket (it may itself be null during rapid connect/disconnect + // churn). Either way, fall back to the primary socket — the same pattern used by + // the BCS_CLOSE_CONNECTION path below. With no socket at all there is nothing to + // close, so bail out. + RakNetSocket2 *closeSocket = (remoteSystemListIndex != -1) ? remoteSystemList[remoteSystemListIndex].rakNetSocket : nullptr; + if (closeSocket == nullptr && socketList.Size() > 0) + closeSocket = socketList[0]; + if (closeSocket == nullptr) + return; + CloseConnectionInternal2(target, sendDisconnectionNotification, false, orderingChannel, disconnectionNotificationPriority, *closeSocket, reasonData); + + // 12/14/09 Return ID_CONNECTION_LOST when calling CloseConnection with sendDisconnectionNotification==false, elsewise it is never returned + if (sendDisconnectionNotification==false && GetConnectionState(target)==IS_CONNECTED) + { + Packet *packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection + packet->guid = target.rakNetGuid==UNASSIGNED_RAKNET_GUID ? GetGuidFromSystemAddress(target.systemAddress) : target.rakNetGuid; + packet->systemAddress = address; + packet->systemAddress.systemIndex = static_cast(remoteSystemListIndex == -1 ? 0 : remoteSystemListIndex); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + packet->wasGeneratedLocally=true; // else processed twice + AddPacketToProducer(packet); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Cancel a pending connection attempt +// If we are already connected, the connection stays open +// \param[in] target Which system to cancel +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::CancelConnectionAttempt( const SystemAddress target ) +{ + unsigned int i; + + // Cancel pending connection attempt, if there is one + i=0; + requestedConnectionQueueMutex.Lock(); + while (i < requestedConnectionQueue.Size()) + { + if (requestedConnectionQueue[i]->systemAddress==target) + { +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Deleting requestedConnectionQueue %i client_handshake %x\n", i, requestedConnectionQueue[ i ]->client_handshake); + MafiaNet::OP_DELETE(requestedConnectionQueue[i]->client_handshake, _FILE_AND_LINE_ ); +#endif + MafiaNet::OP_DELETE(requestedConnectionQueue[i], _FILE_AND_LINE_ ); + requestedConnectionQueue.RemoveAtIndex(i); + break; + } + else + i++; + } + requestedConnectionQueueMutex.Unlock(); + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +ConnectionState RakPeer::GetConnectionState(const AddressOrGUID systemIdentifier) +{ + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + unsigned int i=0; + requestedConnectionQueueMutex.Lock(); + for (; i < requestedConnectionQueue.Size(); i++) + { + if (requestedConnectionQueue[i]->systemAddress==systemIdentifier.systemAddress) + { + requestedConnectionQueueMutex.Unlock(); + return IS_PENDING; + } + } + requestedConnectionQueueMutex.Unlock(); + } + + int index; + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); + } + else + { + index = GetIndexFromGuid(systemIdentifier.rakNetGuid); + } + + if (index==-1) + return IS_NOT_CONNECTED; + + if (remoteSystemList[index].isActive==false) + return IS_DISCONNECTED; + + switch (remoteSystemList[index].connectMode) + { + case RemoteSystemStruct::DISCONNECT_ASAP: + return IS_DISCONNECTING; + case RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY: + return IS_SILENTLY_DISCONNECTING; + case RemoteSystemStruct::DISCONNECT_ON_NO_ACK: + return IS_DISCONNECTING; + case RemoteSystemStruct::REQUESTED_CONNECTION: + return IS_CONNECTING; + case RemoteSystemStruct::HANDLING_CONNECTION_REQUEST: + return IS_CONNECTING; + case RemoteSystemStruct::UNVERIFIED_SENDER: + return IS_CONNECTING; + case RemoteSystemStruct::EXCHANGING_SESSION_DATA: + // Transport is up but the session handshake has not completed, so the application has not been + // told this connection exists yet. + return IS_CONNECTING; + case RemoteSystemStruct::CONNECTED: + return IS_CONNECTED; + default: + return IS_NOT_CONNECTED; + } +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Given a systemAddress, returns an index from 0 to the maximum number of players allowed - 1. +// +// Parameters +// systemAddress - The systemAddress to search for +// +// Returns +// An integer from 0 to the maximum number of peers -1, or -1 if that player is not found +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress ) const +{ + return GetIndexFromSystemAddress(systemAddress, false); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// This function is only useful for looping through all players. +// +// Parameters +// index - an integer between 0 and the maximum number of players allowed - 1. +// +// Returns +// A valid systemAddress or UNASSIGNED_SYSTEM_ADDRESS if no such player at that index +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetSystemAddressFromIndex( unsigned int index ) +{ + // remoteSystemList in user thread + //if ( index >= 0 && index < remoteSystemListSize ) + if ( index < maximumNumberOfPeers ) + if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail + return remoteSystemList[ index ].systemAddress; + + return UNASSIGNED_SYSTEM_ADDRESS; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Same as GetSystemAddressFromIndex but returns RakNetGUID +// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. +// \return The RakNetGUID +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakNetGUID RakPeer::GetGUIDFromIndex( unsigned int index ) +{ + // remoteSystemList in user thread + //if ( index >= 0 && index < remoteSystemListSize ) + if ( index < maximumNumberOfPeers ) + if (remoteSystemList[index].isActive && remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail + return remoteSystemList[ index ].guid; + + return UNASSIGNED_RAKNET_GUID; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Same as calling GetSystemAddressFromIndex and GetGUIDFromIndex for all systems, but more efficient +// Indices match each other, so \a addresses[0] and \a guids[0] refer to the same system +// \param[out] addresses All system addresses. Size of the list is the number of connections. Size of the list will match the size of the \a guids list. +// \param[out] guids All guids. Size of the list is the number of connections. Size of the list will match the size of the \a addresses list. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetSystemList(DataStructures::List &addresses, DataStructures::List &guids) const +{ + addresses.Clear(false, _FILE_AND_LINE_); + guids.Clear(false, _FILE_AND_LINE_); + + if ( remoteSystemList == 0 || endThreads == true ) + return; + + unsigned int i; + for (i=0; i < activeSystemListSize; i++) + { + if ((activeSystemList[i])->isActive && + (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED) + { + addresses.Push((activeSystemList[i])->systemAddress, _FILE_AND_LINE_ ); + guids.Push((activeSystemList[i])->guid, _FILE_AND_LINE_ ); + } + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Bans an IP from connecting. Banned IPs persist between connections. +// +// Parameters +// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban +// All IP addresses starting with 128.0.0 +// milliseconds - how many ms for a temporary ban. Use 0 for a permanent ban +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AddToBanList( const char *IP, MafiaNet::TimeMS milliseconds ) +{ + unsigned index; + MafiaNet::TimeMS time = MafiaNet::GetTimeMS(); + + if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) + return ; + + // If this guy is already in the ban list, do nothing + index = 0; + + banListMutex.Lock(); + + for ( ; index < banList.Size(); index++ ) + { + if ( strcmp( IP, banList[ index ]->IP ) == 0 ) + { + // Already in the ban list. Just update the time + if (milliseconds==0) + banList[ index ]->timeout=0; // Infinite + else + banList[ index ]->timeout=time+milliseconds; + banListMutex.Unlock(); + return; + } + } + + banListMutex.Unlock(); + + BanStruct *banStruct = MafiaNet::OP_NEW( _FILE_AND_LINE_ ); + banStruct->IP = (char*) rakMalloc_Ex( 16, _FILE_AND_LINE_ ); + if (milliseconds==0) + banStruct->timeout=0; // Infinite + else + banStruct->timeout=time+milliseconds; + strcpy_s( banStruct->IP, 16, IP ); + banListMutex.Lock(); + banList.Insert( banStruct, _FILE_AND_LINE_ ); + banListMutex.Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Allows a previously banned IP to connect. +// +// Parameters +// IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban +// All IP addresses starting with 128.0.0 +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RemoveFromBanList( const char *IP ) +{ + unsigned index; + BanStruct *temp; + + if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) + return ; + + index = 0; + temp=0; + + banListMutex.Lock(); + + for ( ; index < banList.Size(); index++ ) + { + if ( strcmp( IP, banList[ index ]->IP ) == 0 ) + { + temp = banList[ index ]; + banList[ index ] = banList[ banList.Size() - 1 ]; + banList.RemoveAtIndex( banList.Size() - 1 ); + break; + } + } + + banListMutex.Unlock(); + + if (temp) + { + rakFree_Ex(temp->IP, _FILE_AND_LINE_ ); + MafiaNet::OP_DELETE(temp, _FILE_AND_LINE_); + } + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Allows all previously banned IPs to connect. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearBanList( void ) +{ + unsigned index; + index = 0; + banListMutex.Lock(); + + for ( ; index < banList.Size(); index++ ) + { + rakFree_Ex(banList[ index ]->IP, _FILE_AND_LINE_ ); + MafiaNet::OP_DELETE(banList[ index ], _FILE_AND_LINE_); + } + + banList.Clear(false, _FILE_AND_LINE_); + + banListMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetLimitIPConnectionFrequency(bool b) +{ + limitConnectionFrequencyFromTheSameIP=b; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Determines if a particular IP is banned. +// +// Parameters +// IP - Complete dotted IP address +// +// Returns +// True if IP matches any IPs in the ban list, accounting for any wildcards. +// False otherwise. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsBanned( const char *IP ) +{ + unsigned banListIndex, characterIndex; + MafiaNet::TimeMS time; + BanStruct *temp; + + if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) + return false; + + banListIndex = 0; + + if ( banList.Size() == 0 ) + return false; // Skip the mutex if possible + + time = MafiaNet::GetTimeMS(); + + banListMutex.Lock(); + + while ( banListIndex < banList.Size() ) + { + if (banList[ banListIndex ]->timeout>0 && banList[ banListIndex ]->timeoutIP, _FILE_AND_LINE_ ); + MafiaNet::OP_DELETE(temp, _FILE_AND_LINE_); + } + else + { + characterIndex = 0; + + for(;;) + { + if ( banList[ banListIndex ]->IP[ characterIndex ] == IP[ characterIndex ] ) + { + // Equal characters + + if ( IP[ characterIndex ] == 0 ) + { + banListMutex.Unlock(); + // End of the string and the strings match + + return true; + } + + characterIndex++; + } + + else + { + if ( banList[ banListIndex ]->IP[ characterIndex ] == 0 || IP[ characterIndex ] == 0 ) + { + // End of one of the strings + break; + } + + // Characters do not match + if ( banList[ banListIndex ]->IP[ characterIndex ] == '*' ) + { + banListMutex.Unlock(); + + // Domain is banned. + return true; + } + + // Characters do not match and it is not a * + break; + } + } + + banListIndex++; + } + } + + banListMutex.Unlock(); + + // No match found. + return false; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Send a ping to the specified connected system. +// +// Parameters: +// target - who to ping +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::Ping( const SystemAddress target ) +{ + PingInternal(target, false, MafiaNet::Reliability::Unreliable); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Send a ping to the specified unconnected system. +// The remote system, if it is Initialized, will respond with ID_UNCONNECTED_PONG. +// The final ping time will be encoded in the following sizeof(MafiaNet::TimeMS) bytes. (Default is 4 bytes - See __GET_TIME_64BIT in types.h +// +// Parameters: +// host: Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. +// remotePort: Which port to connect to on the remote machine. +// onlyReplyOnAcceptingConnections: Only request a reply if the remote system has open connections +// connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex ) +{ + if ( host == 0 ) + return false; + + // If this assert hits then Startup wasn't called or the call failed. + RakAssert(connectionSocketIndex < socketList.Size()); + +// if ( IsActive() == false ) +// return; + + MafiaNet::BitStream bitStream( sizeof(unsigned char) + sizeof(MafiaNet::Time) ); + if ( onlyReplyOnAcceptingConnections ) + bitStream.Write((MessageID)ID_UNCONNECTED_PING_OPEN_CONNECTIONS); + else + bitStream.Write((MessageID)ID_UNCONNECTED_PING); + + bitStream.Write(MafiaNet::GetTime()); + + bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + + bitStream.Write(GetMyGUID()); + + // No timestamp for 255.255.255.255 + unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + /* + + SystemAddress systemAddress; + systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); + systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); + + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( socketList[realIndex], (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + */ + + RNS2_SendParameters bsp; + bsp.data = (char*) bitStream.GetData() ; + bsp.length = bitStream.GetNumberOfBytesUsed(); + bsp.systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); + if (bsp.systemAddress==UNASSIGNED_SYSTEM_ADDRESS) + return false; + bsp.systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), bsp.systemAddress); + socketList[realIndex]->Send(&bsp, _FILE_AND_LINE_); + + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the average of all ping times read for a specified target +// +// Parameters: +// target - whose time to read +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetAveragePing( const AddressOrGUID systemIdentifier ) +{ + int sum, quantity; + RemoteSystemStruct *remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); + + if ( remoteSystem == 0 ) + return -1; + + for ( sum = 0, quantity = 0; quantity < PING_TIMES_ARRAY_SIZE; quantity++ ) + { + if ( remoteSystem->pingAndClockDifferential[ quantity ].pingTime == 65535 ) + break; + else + sum += remoteSystem->pingAndClockDifferential[ quantity ].pingTime; + } + + if ( quantity > 0 ) + return sum / quantity; + else + return -1; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the last ping time read for the specific player or -1 if none read yet +// +// Parameters: +// target - whose time to read +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetLastPing( const AddressOrGUID systemIdentifier ) const +{ + RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); + + if ( remoteSystem == 0 ) + return -1; + +// return (int)(remoteSystem->reliabilityLayer.GetAckPing()/(MafiaNet::TimeUS)1000); + + if ( remoteSystem->pingAndClockDifferentialWriteIndex == 0 ) + return remoteSystem->pingAndClockDifferential[ PING_TIMES_ARRAY_SIZE - 1 ].pingTime; + else + return remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex - 1 ].pingTime; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the lowest ping time read or -1 if none read yet +// +// Parameters: +// target - whose time to read +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetLowestPing( const AddressOrGUID systemIdentifier ) const +{ + RemoteSystemStruct * remoteSystem = GetRemoteSystem( systemIdentifier, false, false ); + + if ( remoteSystem == 0 ) + return -1; + + return remoteSystem->lowestPing; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Ping the remote systems every so often. This is off by default +// This will work anytime +// +// Parameters: +// doPing - True to start occasional pings. False to stop them. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetOccasionalPing( bool doPing ) +{ + occasionalPing = doPing; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +/// Return the clock difference between your system and the specified system +/// Subtract the time from a time returned by the remote system to get that time relative to your own system +/// Returns 0 if the system is unknown +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +MafiaNet::Time RakPeer::GetClockDifferential( const AddressOrGUID systemIdentifier ) +{ + RemoteSystemStruct *remoteSystem = GetRemoteSystem(systemIdentifier, false, false); + if (remoteSystem == 0) + return 0; + return GetClockDifferentialInt(remoteSystem); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +MafiaNet::Time RakPeer::GetClockDifferentialInt(RemoteSystemStruct *remoteSystem) const +{ + int counter, lowestPingSoFar; + MafiaNet::Time clockDifferential; + + lowestPingSoFar = 65535; + + clockDifferential = 0; + + for ( counter = 0; counter < PING_TIMES_ARRAY_SIZE; counter++ ) + { + if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime == 65535 ) + break; + + if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime < lowestPingSoFar ) + { + clockDifferential = remoteSystem->pingAndClockDifferential[ counter ].clockDifferential; + lowestPingSoFar = remoteSystem->pingAndClockDifferential[ counter ].pingTime; + } + } + + return clockDifferential; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Length should be under 400 bytes, as a security measure against flood attacks +// Sets the data to send with an (LAN server discovery) /(offline ping) response +// See the Ping sample project for how this is used. +// data: a block of data to store, or 0 for none +// length: The length of data in bytes, or 0 for none +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetOfflinePingResponse( const char *data, const unsigned int length ) +{ + RakAssert(length < 400); + + rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); + offlinePingResponse.Reset(); + + if ( data && length > 0 ) + offlinePingResponse.Write( data, length ); + + rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns pointers to a copy of the data passed to SetOfflinePingResponse +// \param[out] data A pointer to a copy of the data passed to \a SetOfflinePingResponse() +// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() +// \sa SetOfflinePingResponse +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetOfflinePingResponse( char **data, unsigned int *length ) +{ + rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); + *data = (char*) offlinePingResponse.GetData(); + *length = (int) offlinePingResponse.GetNumberOfBytesUsed(); + rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Sets the payload this peer offers during the session handshake. Opaque to MafiaNet. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetSessionConfig( const char *data, unsigned int length ) +{ + RakAssert(length <= MAXIMUM_SESSION_CONFIG_SIZE); + if (length > MAXIMUM_SESSION_CONFIG_SIZE) + length = MAXIMUM_SESSION_CONFIG_SIZE; + + rakPeerMutexes[ sessionConfig_Mutex ].Lock(); + sessionConfig.Reset(); + + if ( data && length > 0 ) + sessionConfig.Write( data, length ); + + rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetSessionConfig( char **data, unsigned int *length ) +{ + rakPeerMutexes[ sessionConfig_Mutex ].Lock(); + *data = (char*) sessionConfig.GetData(); + *length = (unsigned int) sessionConfig.GetNumberOfBytesUsed(); + rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +const char *RakPeer::GetRemoteSessionConfig( const AddressOrGUID systemIdentifier, unsigned int *length ) +{ + if (length) + *length = 0; + + int index; + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + index = GetIndexFromSystemAddress(systemIdentifier.systemAddress, false); + else + index = GetIndexFromGuid(systemIdentifier.rakNetGuid); + + if (index==-1 || remoteSystemList==0 || remoteSystemList[index].isActive==false) + return 0; + + if (length) + *length = remoteSystemList[index].remoteSessionConfigLength; + return (const char*) remoteSystemList[index].remoteSessionConfigData; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetSessionConfigInteractive( bool interactive ) +{ + sessionConfigInteractive = interactive; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AcceptSession( const AddressOrGUID systemIdentifier, const char *data, unsigned int length ) +{ + RakAssert(length <= MAXIMUM_SESSION_CONFIG_SIZE); + if (length > MAXIMUM_SESSION_CONFIG_SIZE) + length = MAXIMUM_SESSION_CONFIG_SIZE; + QueueSessionDecision(systemIdentifier, true, data, length); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RejectSession( const AddressOrGUID systemIdentifier, const char *reason ) +{ + unsigned int reasonLength = (reason && reason[0]) ? (unsigned int) strlen(reason) : 0; + // Bounded like every other payload that crosses the handshake. A reason is meant to be a short + // string, so an oversized one is a caller mistake rather than something to transmit. + RakAssert(reasonLength <= MAXIMUM_SESSION_CONFIG_SIZE); + if (reasonLength > MAXIMUM_SESSION_CONFIG_SIZE) + reasonLength = MAXIMUM_SESSION_CONFIG_SIZE; + QueueSessionDecision(systemIdentifier, false, reason, reasonLength); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetMaximumPendingSessions( unsigned short total, unsigned short perAddress ) +{ + maximumPendingSessions = total; + maximumPendingSessionsPerAddress = total != 0 ? perAddress : 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetSessionTimeout( MafiaNet::TimeMS timeMS ) +{ + sessionTimeout = timeMS; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendSessionStatus( const AddressOrGUID systemIdentifier, const char *data, unsigned int length ) +{ + RakAssert(length <= MAXIMUM_SESSION_CONFIG_SIZE); + if (length > MAXIMUM_SESSION_CONFIG_SIZE) + length = MAXIMUM_SESSION_CONFIG_SIZE; + QueueSessionCommand(systemIdentifier, BufferedCommandStruct::BCS_SESSION_STATUS, data, length); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Hand an application's accept/reject over to the network thread. Applying it here would send on the +// connection and mutate connectMode from the user thread, which the rest of RakPeer never does. +void RakPeer::QueueSessionDecision( const AddressOrGUID systemIdentifier, bool accept, const char *data, unsigned int length ) +{ + QueueSessionCommand(systemIdentifier, accept ? BufferedCommandStruct::BCS_SESSION_ACCEPT : BufferedCommandStruct::BCS_SESSION_REJECT, data, length); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::QueueSessionCommand( const AddressOrGUID systemIdentifier, int command, const char *data, unsigned int length ) +{ + BufferedCommandStruct *bcs; + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->command = (decltype(bcs->command)) command; + bcs->systemIdentifier = systemIdentifier; + bcs->numberOfBitsToSend = BYTES_TO_BITS(length); + bcs->broadcast = false; + bcs->connectionMode = RemoteSystemStruct::NO_ACTION; + bcs->orderingChannel = 0; + bcs->priority = MafiaNet::Priority::Immediate; + bcs->reliability = MafiaNet::Reliability::ReliableOrdered; + bcs->data = 0; + if (data && length > 0) + { + bcs->data = (char*) rakMalloc_Ex(length, _FILE_AND_LINE_); + if (bcs->data) + memcpy(bcs->data, data, length); + else + bcs->numberOfBitsToSend = 0; + } + bufferedCommands.Push(bcs); + quitAndDataEvents.SetEvent(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Return the unique SystemAddress that represents you on the the network +// Note that unlike in previous versions, this is a struct and is not sequential +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetInternalID( const SystemAddress systemAddress, const int index ) const +{ + if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) + { + return ipList[index]; + } + else + { + +// SystemAddress returnValue; + RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, false, true ); + if (remoteSystem==0) + return UNASSIGNED_SYSTEM_ADDRESS; + + return remoteSystem->theirInternalSystemAddress[index]; + /* + sockaddr_in sa; + socklen_t len = sizeof(sa); + if (getsockname__(connectionSockets[remoteSystem->connectionSocketIndex], (sockaddr*)&sa, &len)!=0) + return UNASSIGNED_SYSTEM_ADDRESS; + returnValue.port=ntohs(sa.sin_port); + returnValue.binaryAddress=sa.sin_addr.s_addr; + return returnValue; +*/ + + + + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +/// \brief Sets your internal IP address, for platforms that do not support reading it, or to override a value +/// \param[in] systemAddress. The address to set. Use SystemAddress::FromString() if you want to use a dotted string +/// \param[in] index When you have multiple internal IDs, which index to set? +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetInternalID(SystemAddress systemAddress, int index) +{ + RakAssert(index >=0 && index < MAXIMUM_NUMBER_OF_INTERNAL_IDS); + ipList[index]=systemAddress; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Return the unique address identifier that represents you on the the network and is based on your external +// IP / port (the IP / port the specified player uses to communicate with you) +// Note that unlike in previous versions, this is a struct and is not sequential +// +// Parameters: +// target: Which remote system you are referring to for your external ID +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetExternalID( const SystemAddress target ) const +{ + unsigned i; + SystemAddress inactiveExternalId; + + inactiveExternalId=UNASSIGNED_SYSTEM_ADDRESS; + + if (target==UNASSIGNED_SYSTEM_ADDRESS) + return firstExternalID; + + // First check for active connection with this systemAddress + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].systemAddress == target ) + { + if ( remoteSystemList[ i ].isActive ) + return remoteSystemList[ i ].myExternalSystemAddress; + else if (remoteSystemList[ i ].myExternalSystemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + inactiveExternalId=remoteSystemList[ i ].myExternalSystemAddress; + } + } + + return inactiveExternalId; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +const RakNetGUID RakPeer::GetMyGUID(void) const +{ + return myGuid; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetMyBoundAddress(const int socketIndex) +{ + DataStructures::List sockets; + GetSockets( sockets ); + if (sockets.Size()>0) + return sockets[socketIndex]->GetBoundAddress(); + else + return UNASSIGNED_SYSTEM_ADDRESS; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +const RakNetGUID& RakPeer::GetGuidFromSystemAddress( const SystemAddress input ) const +{ + if (input==UNASSIGNED_SYSTEM_ADDRESS) + return myGuid; + + if (input.systemIndex!=(SystemIndex)-1 && input.systemIndexreliabilityLayer.SetTimeoutTime(timeMS); + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +MafiaNet::TimeMS RakPeer::GetTimeoutTime( const SystemAddress target ) +{ + if (target==UNASSIGNED_SYSTEM_ADDRESS) + { + return defaultTimeoutTime; + } + else + { + RemoteSystemStruct * remoteSystem = GetRemoteSystemFromSystemAddress( target, false, true ); + + if ( remoteSystem != 0 ) + return remoteSystem->reliabilityLayer.GetTimeoutTime(); + } + return defaultTimeoutTime; +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the current MTU size +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetMTUSize( const SystemAddress target ) const +{ + if (target!=UNASSIGNED_SYSTEM_ADDRESS) + { + RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(target, false, true); + if (rss) + return rss->MTUSize; + } + return defaultMTUSize; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Returns the number of IP addresses we have +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetNumberOfAddresses( void ) +{ + if (IsActive() == false) { + FillIPList(); + } + + unsigned int i = 0; + while (i < MAXIMUM_NUMBER_OF_INTERNAL_IDS && ipList[i] != UNASSIGNED_SYSTEM_ADDRESS) + i++; // count assigned entries until the first unassigned one (end of list) + + return i; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns an IP address at index 0 to GetNumberOfAddresses-1 +// \param[in] index index into the list of IP addresses +// \return The local IP address at this index +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +const char* RakPeer::GetLocalIP( unsigned int index ) +{ + if (IsActive()==false) + { + // Fill out ipList structure + + FillIPList(); + + } + + + static char str[128]; + ipList[index].ToString(false,str,static_cast(128)); + return str; + + + + +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Is this a local IP? +// \param[in] An IP address to check +// \return True if this is one of the IP addresses returned by GetLocalIP +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsLocalIP( const char *ip ) +{ + if (ip==0 || ip[0]==0) + return false; + + // #med - this should also check for "::1" here in IPv6 mode + if (strcmp(ip, "127.0.0.1")==0 || strcmp(ip, "localhost")==0) + return true; + + int num = GetNumberOfAddresses(); + int i; + for (i=0; i < num; i++) + { + if (strcmp(ip, GetLocalIP(i))==0) + return true; + } + + + + + return false; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary +// when connection to servers with multiple IP addresses +// +// Parameters: +// allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AllowConnectionResponseIPMigration( bool allow ) +{ + allowConnectionResponseIPMigration = allow; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Description: +// Sends a message ID_ADVERTISE_SYSTEM to the remote unconnected system. +// This will tell the remote system our external IP outside the LAN, and can be used for NAT punch through +// +// Requires: +// The sender and recipient must already be started via a successful call to Initialize +// +// host: Either a dotted IP address or a domain name +// remotePort: Which port to connect to on the remote machine. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex ) +{ + MafiaNet::BitStream bs; + bs.Write((MessageID)ID_ADVERTISE_SYSTEM); + bs.WriteAlignedBytes((const unsigned char*) data,dataLength); + return SendOutOfBand(host, remotePort, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), connectionSocketIndex ); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. +// ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived +// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. +// Defaults to 0 (never return this notification) +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetSplitMessageProgressInterval(int interval) +{ + RakAssert(interval>=0); + splitMessageProgressInterval=interval; + for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) + remoteSystemList[ i ].reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns what was passed to SetSplitMessageProgressInterval() +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetSplitMessageProgressInterval(void) const +{ + return splitMessageProgressInterval; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Set how long to wait before giving up on sending an unreliable message +// Useful if the network is clogged up. +// Set to 0 or less to never timeout. Defaults to 0. +// timeoutMS How many ms to wait before simply not sending an unreliable message. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetUnreliableTimeout(MafiaNet::TimeMS timeoutMS) +{ + unreliableTimeout=timeoutMS; + for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) + remoteSystemList[ i ].reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Send a message to host, with the IP socket option TTL set to 3 +// This message will not reach the host, but will open the router. +// Used for NAT-Punchthrough +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex ) +{ + char fakeData[2]; + fakeData[0]=0; + fakeData[1]=1; + unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + if (socketList[realIndex]->IsBerkleySocket()) + { + RNS2_SendParameters bsp; + bsp.data = (char*) fakeData; + bsp.length = 2; + bsp.systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); + bsp.systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); + bsp.ttl=ttl; + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*)bsp.data, BYTES_TO_BITS(bsp.length), bsp.systemAddress); + socketList[realIndex]->Send(&bsp, _FILE_AND_LINE_); + } +} + + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Attatches a Plugin interface to run code automatically on message receipt in the Receive call +// +// \param messageHandler Pointer to a plugin to attach +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AttachPlugin( PluginInterface2 *plugin ) +{ + if (IsInsideNetworkThreadReceive()) + return; + + bool isNotThreadsafe = plugin->UsesReliabilityLayer(); + if (isNotThreadsafe) + { + if (pluginListNTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) + { + plugin->SetRakPeerInterface(this); + plugin->OnAttach(); + pluginListNTS.Insert(plugin, _FILE_AND_LINE_); + } + } + else + { + if (pluginListTS.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) + { + plugin->SetRakPeerInterface(this); + plugin->OnAttach(); + pluginListTS.Insert(plugin, _FILE_AND_LINE_); + } + } + + pluginListNetworkThreadMutex.Lock(); + if (pluginListNetworkThread.GetIndexOf(plugin)==MAX_UNSIGNED_LONG) + pluginListNetworkThread.Insert(plugin, _FILE_AND_LINE_); + pluginListNetworkThreadMutex.Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Detaches a Plugin interface to run code automatically on message receipt +// +// \param messageHandler Pointer to a plugin to detach +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DetachPlugin( PluginInterface2 *plugin ) +{ + if (plugin==0 || IsInsideNetworkThreadReceive()) + return; + + unsigned int index; + + bool isNotThreadsafe = plugin->UsesReliabilityLayer(); + if (isNotThreadsafe) + { + index = pluginListNTS.GetIndexOf(plugin); + if (index!=MAX_UNSIGNED_LONG) + { + // Unordered list so delete from end for speed + pluginListNTS[index]=pluginListNTS[pluginListNTS.Size()-1]; + pluginListNTS.RemoveFromEnd(); + } + } + else + { + index = pluginListTS.GetIndexOf(plugin); + if (index!=MAX_UNSIGNED_LONG) + { + // Unordered list so delete from end for speed + pluginListTS[index]=pluginListTS[pluginListTS.Size()-1]; + pluginListTS.RemoveFromEnd(); + } + } + // Removed under the lock the update thread dispatches under, so once this returns the plugin + // is not inside OnReceiveNetworkThread and will not be called again. + pluginListNetworkThreadMutex.Lock(); + index = pluginListNetworkThread.GetIndexOf(plugin); + if (index!=MAX_UNSIGNED_LONG) + { + pluginListNetworkThread[index]=pluginListNetworkThread[pluginListNetworkThread.Size()-1]; + pluginListNetworkThread.RemoveFromEnd(); + } + pluginListNetworkThreadMutex.Unlock(); + + plugin->OnDetach(); + plugin->SetRakPeerInterface(0); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Put a packet back at the end of the receive queue in case you don't want to deal with it immediately +// +// packet The packet you want to push back. +// pushAtHead True to push the packet so that the next receive call returns it. False to push it at the end of the queue (obviously pushing it at the end makes the packets out of order) +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::PushBackPacket( Packet *packet, bool pushAtHead) +{ + if (packet==0) + return; + + unsigned i; + for (i=0; i < pluginListTS.Size(); i++) + pluginListTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnPushBackPacket((const char*) packet->data, packet->bitSize, packet->systemAddress); + + packetReturnMutex.Lock(); + if (pushAtHead) + packetReturnQueue.PushAtHead(packet,0,_FILE_AND_LINE_); + else + packetReturnQueue.Push(packet,_FILE_AND_LINE_); + packetReturnMutex.Unlock(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ChangeSystemAddress(RakNetGUID guid, const SystemAddress &systemAddress) +{ + BufferedCommandStruct *bcs; + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->data = 0; + bcs->systemIdentifier.systemAddress=systemAddress; + bcs->systemIdentifier.rakNetGuid=guid; + bcs->command=BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS; + bufferedCommands.Push(bcs); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +Packet* RakPeer::AllocatePacket(unsigned dataSize) +{ + return AllocPacket(dataSize, _FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakNetSocket2* RakPeer::GetSocket( const SystemAddress target ) +{ + // Send a query to the thread to get the socket, and return when we got it + BufferedCommandStruct *bcs; + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; + bcs->systemIdentifier=target; + bcs->data=0; + bufferedCommands.Push(bcs); + + // Block up to one second to get the socket, although it should actually take virtually no time + SocketQueryOutput *sqo; + MafiaNet::TimeMS stopWaiting = MafiaNet::GetTimeMS()+1000; + DataStructures::List output; + while (manualUpdate || MafiaNet::GetTimeMS() < stopWaiting) + { + if (manualUpdate) + RunManualUpdate(); // answers the query on this thread + else if (isMainLoopThreadActive==false) + return 0; + else + RakSleep(0); + + sqo = socketQueryOutput.Pop(); + if (sqo) + { + output=sqo->sockets; + sqo->sockets.Clear(false, _FILE_AND_LINE_); + socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); + if (output.Size()) + return output[0]; + break; + } + } + return 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetSockets( DataStructures::List &sockets ) +{ + sockets.Clear(false, _FILE_AND_LINE_); + + // Send a query to the thread to get the socket, and return when we got it + BufferedCommandStruct *bcs; + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->command=BufferedCommandStruct::BCS_GET_SOCKET; + bcs->systemIdentifier=UNASSIGNED_SYSTEM_ADDRESS; + bcs->data=0; + bufferedCommands.Push(bcs); + + // Block up to one second to get the socket, although it should actually take virtually no time + SocketQueryOutput *sqo; +// RakNetSocket2* output; + for(;;) + { + if (manualUpdate) + RunManualUpdate(); // answers the query on this thread + else if (isMainLoopThreadActive==false) + return; + else + RakSleep(0); + + sqo = socketQueryOutput.Pop(); + if (sqo) + { + sockets=sqo->sockets; + sqo->sockets.Clear(false, _FILE_AND_LINE_); + socketQueryOutput.Deallocate(sqo, _FILE_AND_LINE_); + return; + } + } + return; +} +void RakPeer::ReleaseSockets( DataStructures::List &sockets ) +{ + sockets.Clear(false,_FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Adds simulated ping and packet loss to the outgoing data flow. +// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and maxSendBPS value on each. +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ApplyNetworkSimulator( float packetloss, unsigned short minExtraPing, unsigned short extraPingVariance) +{ +#ifndef _DEBUG + // unused parameters + (void)packetloss; + (void)minExtraPing; + (void)extraPingVariance; +#endif + +#ifdef _DEBUG + if (remoteSystemList) + { + unsigned short i; + for (i=0; i < maximumNumberOfPeers; i++) + //for (i=0; i < remoteSystemListSize; i++) + remoteSystemList[i].reliabilityLayer.ApplyNetworkSimulator(packetloss, minExtraPing, extraPingVariance); + } + + _packetloss=packetloss; + _minExtraPing=minExtraPing; + _extraPingVariance=extraPingVariance; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void RakPeer::SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond ) +{ + maxOutgoingBPS=maxBitsPerSecond; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Returns if you previously called ApplyNetworkSimulator +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsNetworkSimulatorActive( void ) +{ +#ifdef _DEBUG + return _packetloss>0 || _minExtraPing>0 || _extraPingVariance>0; +#else + return false; +#endif +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::WriteOutOfBandHeader(MafiaNet::BitStream *bitStream) +{ + bitStream->Write((MessageID)ID_OUT_OF_BAND_INTERNAL); + bitStream->Write(myGuid); + bitStream->WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetUserUpdateThread(void (*_userUpdateThreadPtr)(RakPeerInterface *, void *), void *_userUpdateThreadData) +{ + userUpdateThreadPtr=_userUpdateThreadPtr; + userUpdateThreadData=_userUpdateThreadData; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetSocketFactory(RakNetSocket2Factory *factory) +{ + RakAssert(IsActive() == false); + socketFactory = factory; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetManualUpdate(bool manual) +{ + RakAssert(IsActive() == false); + manualUpdate = manual; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RunManualUpdate() +{ + RakAssert(manualUpdate); + // The same steps as one pass of UpdateNetworkLoop. + if (userUpdateThreadPtr) + userUpdateThreadPtr(this, userUpdateThreadData); + BitStream updateBitStream( MAXIMUM_MTU_SIZE +#if LIBCAT_SECURITY==1 + + cat::AuthenticatedEncryption::OVERHEAD_BYTES +#endif + ); + RunUpdateCycle(updateBitStream); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetIncomingDatagramEventHandler( bool (*_incomingDatagramEventHandler)(RNS2RecvStruct *) ) +{ + incomingDatagramEventHandler=_incomingDatagramEventHandler; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::SendOutOfBand(const char *host, unsigned short remotePort, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex ) +{ + if ( IsActive() == false ) + return false; + + if (host==0 || host[0]==0) + return false; + + // If this assert hits then Startup wasn't called or the call failed. + RakAssert(connectionSocketIndex < socketList.Size()); + + // This is a security measure. Don't send data longer than this value + RakAssert(dataLength <= (MAX_OFFLINE_DATA_LENGTH + sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID))); + + if (host==0) + return false; + + // 34 bytes + MafiaNet::BitStream bitStream; + WriteOutOfBandHeader(&bitStream); + + if (dataLength>0) + { + bitStream.Write(data, dataLength); + } + + unsigned int realIndex = GetRakNetSocketFromUserConnectionSocketIndex(connectionSocketIndex); + + /* + SystemAddress systemAddress; + systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); + systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); + + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), systemAddress); + + SocketLayer::SendTo( socketList[realIndex], (const char*)bitStream.GetData(), (int) bitStream.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + */ + + RNS2_SendParameters bsp; + bsp.data = (char*) bitStream.GetData(); + bsp.length = bitStream.GetNumberOfBytesUsed(); + bsp.systemAddress.FromStringExplicitPort(host,remotePort, socketList[realIndex]->GetBoundAddress().GetIPVersion()); + bsp.systemAddress.FixForIPVersion(socketList[realIndex]->GetBoundAddress()); + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*)bsp.data, BYTES_TO_BITS(bsp.length), bsp.systemAddress); + socketList[realIndex]->Send(&bsp, _FILE_AND_LINE_); + + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakNetStatistics * RakPeer::GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns ) +{ + static RakNetStatistics staticStatistics; + RakNetStatistics *systemStats; + if (rns==0) + systemStats=&staticStatistics; + else + systemStats=rns; + + if (systemAddress==UNASSIGNED_SYSTEM_ADDRESS) + { + bool firstWrite=false; + // Return a crude sum + for ( unsigned short i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].isActive) + { + RakNetStatistics rnsTemp; + remoteSystemList[ i ].reliabilityLayer.GetStatistics(&rnsTemp); + + if (firstWrite==false) + { + memcpy(systemStats, &rnsTemp, sizeof(RakNetStatistics)); + firstWrite=true; + } + else + (*systemStats)+=rnsTemp; + } + } + return systemStats; + } + else + { + RemoteSystemStruct * rss; + rss = GetRemoteSystemFromSystemAddress( systemAddress, false, false ); + if ( rss && endThreads==false ) + { + rss->reliabilityLayer.GetStatistics(systemStats); + return systemStats; + } + } + + return 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GetStatisticsList(DataStructures::List &addresses, DataStructures::List &guids, DataStructures::List &statistics) +{ + addresses.Clear(false, _FILE_AND_LINE_); + guids.Clear(false, _FILE_AND_LINE_); + statistics.Clear(false, _FILE_AND_LINE_); + + if ( remoteSystemList == 0 || endThreads == true ) + return; + + unsigned int i; + for (i=0; i < activeSystemListSize; i++) + { + if ((activeSystemList[i])->isActive && + (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED) + { + addresses.Push((activeSystemList[i])->systemAddress, _FILE_AND_LINE_ ); + guids.Push((activeSystemList[i])->guid, _FILE_AND_LINE_ ); + RakNetStatistics rns; + (activeSystemList[i])->reliabilityLayer.GetStatistics(&rns); + statistics.Push(rns, _FILE_AND_LINE_); + } + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::GetStatistics( const unsigned int index, RakNetStatistics *rns ) +{ + if (index < maximumNumberOfPeers && remoteSystemList[ index ].isActive) + { + remoteSystemList[ index ].reliabilityLayer.GetStatistics(rns); + return true; + } + return false; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetReceiveBufferSize(void) +{ + unsigned int size; + packetReturnMutex.Lock(); + size=packetReturnQueue.Size(); + packetReturnMutex.Unlock(); + return size; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetIndexFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread ) const +{ + unsigned i; + + if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) + return -1; + + if (systemAddress.systemIndex!=(SystemIndex)-1 && systemAddress.systemIndex < maximumNumberOfPeers && remoteSystemList[systemAddress.systemIndex].systemAddress==systemAddress && remoteSystemList[ systemAddress.systemIndex ].isActive) + return systemAddress.systemIndex; + + if (calledFromNetworkThread) + { + return GetRemoteSystemIndex(systemAddress); + } + else + { + // remoteSystemList in user and network thread + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].systemAddress == systemAddress ) + return i; + + // If no active results found, try previously active results. + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].systemAddress == systemAddress ) + return i; + } + + return -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +int RakPeer::GetIndexFromGuid( const RakNetGUID guid ) +{ + unsigned i; + + if ( guid == UNASSIGNED_RAKNET_GUID ) + return -1; + + if (guid.systemIndex!=(SystemIndex)-1 && guid.systemIndex < maximumNumberOfPeers && remoteSystemList[guid.systemIndex].guid==guid && remoteSystemList[ guid.systemIndex ].isActive) + return guid.systemIndex; + + // remoteSystemList in user and network thread + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].isActive && remoteSystemList[ i ].guid == guid ) + return i; + + // If no active results found, try previously active results. + for ( i = 0; i < maximumNumberOfPeers; i++ ) + if ( remoteSystemList[ i ].guid == guid ) + return i; + + return -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +#if LIBCAT_SECURITY==1 +bool RakPeer::GenerateConnectionRequestChallenge(RequestedConnectionStruct *rcs,PublicKey *publicKey) +{ + CAT_AUDIT_PRINTF("AUDIT: In GenerateConnectionRequestChallenge()\n"); + + rcs->client_handshake = 0; + rcs->publicKeyMode = PKM_INSECURE_CONNECTION; + + if (!publicKey) return true; + + switch (publicKey->publicKeyMode) + { + default: + case PKM_INSECURE_CONNECTION: + break; + + case PKM_ACCEPT_ANY_PUBLIC_KEY: + CAT_OBJCLR(rcs->remote_public_key); + rcs->client_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); + + rcs->publicKeyMode = PKM_ACCEPT_ANY_PUBLIC_KEY; + break; + + case PKM_USE_TWO_WAY_AUTHENTICATION: + if (publicKey->myPublicKey == 0 || publicKey->myPrivateKey == 0 || + publicKey->remoteServerPublicKey == 0) + { + return false; + } + + rcs->client_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); + memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); + + if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || + !rcs->client_handshake->SetIdentity(publicKey->myPublicKey, publicKey->myPrivateKey) || + !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) + { + CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object with identity for this RequestedConnectionStruct\n"); + MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + rcs->client_handshake=0; + return false; + } + + CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object with identity for this RequestedConnectionStruct -- pre-generated challenge\n"); + + rcs->publicKeyMode = PKM_USE_TWO_WAY_AUTHENTICATION; + break; + + case PKM_USE_KNOWN_PUBLIC_KEY: + if (publicKey->remoteServerPublicKey == 0) + return false; + + rcs->client_handshake = MafiaNet::OP_NEW(_FILE_AND_LINE_); + memcpy(rcs->remote_public_key, publicKey->remoteServerPublicKey, cat::EasyHandshake::PUBLIC_KEY_BYTES); + + if (!rcs->client_handshake->Initialize(publicKey->remoteServerPublicKey) || + !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) + { + CAT_AUDIT_PRINTF("AUDIT: Failure initializing new client_handshake object for this RequestedConnectionStruct\n"); + MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + rcs->client_handshake=0; + return false; + } + + CAT_AUDIT_PRINTF("AUDIT: Success initializing new client handshake object for this RequestedConnectionStruct -- pre-generated challenge\n"); + + rcs->publicKeyMode = PKM_USE_KNOWN_PUBLIC_KEY; + break; + } + + return true; +} +#endif +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime ) +{ + RakAssert(passwordDataLength <= 256); + RakAssert(remotePort!=0); + SystemAddress systemAddress; + if (!systemAddress.FromStringExplicitPort(host,remotePort,socketList[connectionSocketIndex]->GetBoundAddress().GetIPVersion())) + return CANNOT_RESOLVE_DOMAIN_NAME; + + // Already connected? + if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) + return ALREADY_CONNECTED_TO_ENDPOINT; + + //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); + RequestedConnectionStruct *rcs = MafiaNet::OP_NEW(_FILE_AND_LINE_); + + rcs->systemAddress=systemAddress; + rcs->nextRequestTime= MafiaNet::GetTimeMS(); + rcs->requestsMade=0; + rcs->data=0; + rcs->socket=0; + rcs->extraData=extraData; + rcs->socketIndex=connectionSocketIndex; + rcs->actionToTake=RequestedConnectionStruct::CONNECT; + rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; + rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; + // No password means passwordData is null. memcpy's source is declared nonnull, + // so a null source is undefined behaviour even with a length of 0. + if (passwordDataLength > 0) + memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); + rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; + rcs->timeoutTime=timeoutTime; + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: In SendConnectionRequest()\n"); + if (!GenerateConnectionRequestChallenge(rcs,publicKey)) + return SECURITY_INITIALIZATION_FAILED; +#else + (void) publicKey; +#endif + + // Return false if already pending, else push on queue + unsigned int i=0; + requestedConnectionQueueMutex.Lock(); + for (; i < requestedConnectionQueue.Size(); i++) + { + if (requestedConnectionQueue[i]->systemAddress==systemAddress) + { + requestedConnectionQueueMutex.Unlock(); + // Not necessary + //MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); + return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; + } + } + requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); + requestedConnectionQueueMutex.Unlock(); + + return CONNECTION_ATTEMPT_STARTED; +} +ConnectionAttemptResult RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, PublicKey *publicKey, unsigned connectionSocketIndex, unsigned int extraData, unsigned sendConnectionAttemptCount, unsigned timeBetweenSendConnectionAttemptsMS, MafiaNet::TimeMS timeoutTime, RakNetSocket2* socket ) +{ + RakAssert(passwordDataLength <= 256); + SystemAddress systemAddress; + systemAddress.FromStringExplicitPort(host,remotePort); + + // Already connected? + if (GetRemoteSystemFromSystemAddress(systemAddress, false, true)) + return ALREADY_CONNECTED_TO_ENDPOINT; + + //RequestedConnectionStruct *rcs = (RequestedConnectionStruct *) rakMalloc_Ex(sizeof(RequestedConnectionStruct), _FILE_AND_LINE_); + RequestedConnectionStruct *rcs = MafiaNet::OP_NEW(_FILE_AND_LINE_); + + rcs->systemAddress=systemAddress; + rcs->nextRequestTime= MafiaNet::GetTimeMS(); + rcs->requestsMade=0; + rcs->data=0; + rcs->socket=0; + rcs->extraData=extraData; + rcs->socketIndex=connectionSocketIndex; + rcs->actionToTake=RequestedConnectionStruct::CONNECT; + rcs->sendConnectionAttemptCount=sendConnectionAttemptCount; + rcs->timeBetweenSendConnectionAttemptsMS=timeBetweenSendConnectionAttemptsMS; + // No password means passwordData is null. memcpy's source is declared nonnull, + // so a null source is undefined behaviour even with a length of 0. + if (passwordDataLength > 0) + memcpy(rcs->outgoingPassword, passwordData, passwordDataLength); + rcs->outgoingPasswordLength=(unsigned char) passwordDataLength; + rcs->timeoutTime=timeoutTime; + rcs->socket=socket; + +#if LIBCAT_SECURITY==1 + if (!GenerateConnectionRequestChallenge(rcs,publicKey)) + return SECURITY_INITIALIZATION_FAILED; +#else + (void) publicKey; +#endif + + // Return false if already pending, else push on queue + unsigned int i=0; + requestedConnectionQueueMutex.Lock(); + for (; i < requestedConnectionQueue.Size(); i++) + { + if (requestedConnectionQueue[i]->systemAddress==systemAddress) + { + requestedConnectionQueueMutex.Unlock(); + // Not necessary + //MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); + return CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS; + } + } + requestedConnectionQueue.Push(rcs, _FILE_AND_LINE_ ); + requestedConnectionQueueMutex.Unlock(); + + return CONNECTION_ATTEMPT_STARTED; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ValidateRemoteSystemLookup(void) const +{ +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystem( const AddressOrGUID systemIdentifier, bool calledFromNetworkThread, bool onlyActive ) const +{ + if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) + return GetRemoteSystemFromGUID(systemIdentifier.rakNetGuid, onlyActive); + else + return GetRemoteSystemFromSystemAddress(systemIdentifier.systemAddress, calledFromNetworkThread, onlyActive); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread, bool onlyActive ) const +{ + unsigned i; + + if ( systemAddress == UNASSIGNED_SYSTEM_ADDRESS ) + return 0; + + if (calledFromNetworkThread) + { + unsigned int index = GetRemoteSystemIndex(systemAddress); + if (index!=(unsigned int) -1) + { + if (onlyActive==false || remoteSystemList[ index ].isActive==true ) + { + RakAssert(remoteSystemList[index].systemAddress==systemAddress); + return remoteSystemList + index; + } + } + } + else + { + int deadConnectionIndex=-1; + + // Active connections take priority. But if there are no active connections, return the first systemAddress match found + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].systemAddress == systemAddress) + { + if ( remoteSystemList[ i ].isActive ) + return remoteSystemList + i; + else if (deadConnectionIndex==-1) + deadConnectionIndex=i; + } + } + + if (deadConnectionIndex!=-1 && onlyActive==false) + return remoteSystemList + deadConnectionIndex; + } + + return 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromGUID( const RakNetGUID guid, bool onlyActive ) const +{ + if (guid==UNASSIGNED_RAKNET_GUID) + return 0; + + unsigned i; + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if (remoteSystemList[ i ].guid == guid && (onlyActive==false || remoteSystemList[ i ].isActive)) + { + return remoteSystemList + i; + } + } + return 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ParseConnectionRequestPacket( RakPeer::RemoteSystemStruct *remoteSystem, const SystemAddress &systemAddress, const char *data, int byteSize ) +{ + MafiaNet::BitStream bs((unsigned char*) data,byteSize,false); + bs.IgnoreBytes(sizeof(MessageID)); + RakNetGUID guid; + bs.Read(guid); + MafiaNet::Time incomingTimestamp; + bs.Read(incomingTimestamp); + unsigned char doSecurity; + bs.Read(doSecurity); + +#if LIBCAT_SECURITY==1 + unsigned char doClientKey; + if (_using_security) + { + // Ignore message on bad state + if (doSecurity != 1 || !remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()) + return; + + // Validate client proof of key + unsigned char proof[cat::EasyHandshake::PROOF_BYTES]; + bs.ReadAlignedBytes(proof, sizeof(proof)); + if (!remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->ValidateProof(proof, sizeof(proof))) + { + remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + + CAT_OBJCLR(remoteSystem->client_public_key); + + bs.Read(doClientKey); + + // Check if client wants to prove identity + if (doClientKey == 1) + { + // Read identity proof + unsigned char ident[cat::EasyHandshake::IDENTITY_BYTES]; + bs.ReadAlignedBytes(ident, sizeof(ident)); + + // If we are listening to these proofs, + if (_require_client_public_key) + { + // Validate client identity + if (!_server_handshake->VerifyInitiatorIdentity(remoteSystem->answer, ident, remoteSystem->client_public_key)) + { + MafiaNet::BitStream bitStream; + bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect + bitStream.Write((unsigned char)2); // Indicate client identity is invalid + SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); + remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + } + + // Otherwise ignore the client public key + } + else + { + // If no client key was provided but it is required, + if (_require_client_public_key) + { + MafiaNet::BitStream bitStream; + bitStream.Write((MessageID)ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); // Report an error since the client is not providing an identity when it is necessary to connect + bitStream.Write((unsigned char)1); // Indicate client identity is missing + SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); + remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + } + } +#endif // LIBCAT_SECURITY + + unsigned char *password = bs.GetData()+BITS_TO_BYTES(bs.GetReadOffset()); + int passwordLength = byteSize - BITS_TO_BYTES(bs.GetReadOffset()); + if ( incomingPasswordLength != passwordLength || + memcmp( password, incomingPassword, incomingPasswordLength ) != 0 ) + { + CAT_AUDIT_PRINTF("AUDIT: Invalid password\n"); + // This one we only send once since we don't care if it arrives. + MafiaNet::BitStream bitStream; + bitStream.Write((MessageID)ID_INVALID_PASSWORD); + bitStream.Write(GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + SendImmediate((char*) bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + return; + } + + // OK + remoteSystem->connectMode=RemoteSystemStruct::HANDLING_CONNECTION_REQUEST; + + OnConnectionRequest( remoteSystem, incomingTimestamp ); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::OnConnectionRequest( RakPeer::RemoteSystemStruct *remoteSystem, MafiaNet::Time incomingTimestamp ) +{ + MafiaNet::BitStream bitStream; + bitStream.Write((MessageID)ID_CONNECTION_REQUEST_ACCEPTED); + bitStream.Write(remoteSystem->systemAddress); + SystemIndex systemIndex = (SystemIndex) GetIndexFromSystemAddress( remoteSystem->systemAddress, true ); + RakAssert(systemIndex!=65535); + bitStream.Write(systemIndex); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + bitStream.Write(ipList[i]); + bitStream.Write(incomingTimestamp); + bitStream.Write(MafiaNet::GetTime()); + + SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, remoteSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::NotifyAndFlagForShutdown( const SystemAddress systemAddress, bool performImmediate, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority, const MafiaNet::BitStream *reasonData ) +{ + MafiaNet::BitStream temp( sizeof(unsigned char) ); + temp.Write( (MessageID)ID_DISCONNECTION_NOTIFICATION ); + // Optionally append a caller-supplied reason payload right after the 1-byte ID. The ID occupies exactly 8 bits so + // the payload stays byte-aligned; the remote reads it from packet->data+1 (length packet->length-1). Wire-backward + // compatible: peers that only inspect data[0] ignore the extra bytes. + if (reasonData != nullptr && reasonData->GetNumberOfBytesUsed() > 0) + temp.Write( (const char*)reasonData->GetData(), reasonData->GetNumberOfBytesUsed() ); + if (performImmediate) + { + SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, MafiaNet::Reliability::ReliableOrdered, orderingChannel, systemAddress, false, false, MafiaNet::GetTimeUS(), 0); + RemoteSystemStruct *rss=GetRemoteSystemFromSystemAddress(systemAddress, true, true); + rss->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; + } + else + { + SendBuffered((const char*)temp.GetData(), temp.GetNumberOfBitsUsed(), disconnectionNotificationPriority, MafiaNet::Reliability::ReliableOrdered, orderingChannel, systemAddress, false, RemoteSystemStruct::DISCONNECT_ASAP, 0); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearDisconnectReason( RemoteSystemStruct *remoteSystem ) +{ + if (remoteSystem==0) + return; + if (remoteSystem->disconnectReasonData!=0) + { + rakFree_Ex(remoteSystem->disconnectReasonData, _FILE_AND_LINE_); + remoteSystem->disconnectReasonData=0; + } + remoteSystem->disconnectReasonLength=0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearSessionConfig( RemoteSystemStruct *remoteSystem ) +{ + if (remoteSystem==0) + return; + if (remoteSystem->remoteSessionConfigData!=0) + { + rakFree_Ex(remoteSystem->remoteSessionConfigData, _FILE_AND_LINE_); + remoteSystem->remoteSessionConfigData=0; + } + remoteSystem->remoteSessionConfigLength=0; + if (remoteSystem->withheldConnectionPacketData!=0) + { + rakFree_Ex(remoteSystem->withheldConnectionPacketData, _FILE_AND_LINE_); + remoteSystem->withheldConnectionPacketData=0; + } + remoteSystem->withheldConnectionPacketLength=0; + remoteSystem->sessionConfigAwaitingLocalDecision=false; + remoteSystem->sessionConfigIsConnectingSide=false; + remoteSystem->connectionReportedToApplication=false; + remoteSystem->sessionConfigStartTime=0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::StoreRemoteSessionConfig( RemoteSystemStruct *remoteSystem, const unsigned char *data, unsigned int length ) +{ + if (remoteSystem==0) + return false; + + // An oversized payload is a protocol violation, not something to truncate: the sender enforces the + // same cap in SetSessionConfig, so anything larger did not come from a well-behaved peer. + if (length > MAXIMUM_SESSION_CONFIG_SIZE) + return false; + + if (remoteSystem->remoteSessionConfigData!=0) + { + rakFree_Ex(remoteSystem->remoteSessionConfigData, _FILE_AND_LINE_); + remoteSystem->remoteSessionConfigData=0; + } + remoteSystem->remoteSessionConfigLength=0; + + if (data==0 || length==0) + return true; + + // One extra byte, always zeroed and never counted in the reported length. The payload is + // attacker-controlled and arbitrary bytes, so an application that reaches for strlen/printf on it + // would otherwise run off the end of the buffer. Length remains the authoritative bound. + remoteSystem->remoteSessionConfigData = (unsigned char*) rakMalloc_Ex(length + 1, _FILE_AND_LINE_); + if (remoteSystem->remoteSessionConfigData==0) + return false; + + memcpy(remoteSystem->remoteSessionConfigData, data, length); + remoteSystem->remoteSessionConfigData[length] = 0; + remoteSystem->remoteSessionConfigLength = length; + return true; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Replay the connection packet that was held back while the session handshake ran. This is the single +// point at which an application learns a connection exists, and by construction it runs only once the +// remote's session payload is already stored on the slot. +void RakPeer::ProduceWithheldConnectionPacket( RemoteSystemStruct *remoteSystem, MessageID messageId ) +{ + if (remoteSystem==0) + return; + + Packet *packet; + if (remoteSystem->withheldConnectionPacketData!=0 && remoteSystem->withheldConnectionPacketLength>0) + { + packet=AllocPacket(remoteSystem->withheldConnectionPacketLength, _FILE_AND_LINE_); + memcpy(packet->data, remoteSystem->withheldConnectionPacketData, remoteSystem->withheldConnectionPacketLength); + packet->bitSize = remoteSystem->withheldConnectionPacketLength * 8; + rakFree_Ex(remoteSystem->withheldConnectionPacketData, _FILE_AND_LINE_); + remoteSystem->withheldConnectionPacketData=0; + remoteSystem->withheldConnectionPacketLength=0; + } + else + { + // The original bytes could not be stashed (allocation failure); the message id alone still tells + // the application a connection exists, which is what every caller keys off. + packet=AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); + packet->data[0]=messageId; + packet->bitSize = sizeof(MessageID) * 8; + } + + packet->systemAddress = remoteSystem->systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex = packet->systemAddress.systemIndex; + remoteSystem->connectionReportedToApplication=true; + AddPacketToProducer(packet); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ProduceSessionAbandoned( RemoteSystemStruct *remoteSystem ) +{ + if (remoteSystem==0 || remoteSystem->weInitiatedTheConnection || remoteSystem->sessionConfigAwaitingLocalDecision==false) + return; + + // Once only: the flag is what made the request the application's to answer. + remoteSystem->sessionConfigAwaitingLocalDecision=false; + + Packet *packet=AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); + packet->data[0]=(unsigned char)ID_SESSION_CONFIG_ABANDONED; + packet->bitSize=sizeof(MessageID)*8; + packet->systemAddress=remoteSystem->systemAddress; + packet->systemAddress.systemIndex=remoteSystem->remoteSystemIndex; + packet->guid=remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Server side of the session handshake: answer the client's request, promote the connection to +// CONNECTED and release the ID_NEW_INCOMING_CONNECTION that was withheld. +void RakPeer::SendSessionConfigResponse( RemoteSystemStruct *remoteSystem, const char *data, unsigned int length ) +{ + if (remoteSystem==0) + return; + + MafiaNet::BitStream bitStream; + bitStream.Write((MessageID)ID_SESSION_CONFIG); + if (data && length > 0) + bitStream.Write(data, length); + SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, remoteSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); + + remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; + remoteSystem->sessionConfigStartTime=0; + ProduceWithheldConnectionPacket(remoteSystem, (MessageID)ID_NEW_INCOMING_CONNECTION); + + PingInternal( remoteSystem->systemAddress, true, MafiaNet::Reliability::Unreliable ); + quitAndDataEvents.SetEvent(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendSessionConfigRejection( RemoteSystemStruct *remoteSystem, const char *reason, unsigned int reasonLength ) +{ + if (remoteSystem==0) + return; + + MafiaNet::BitStream bitStream; + bitStream.Write((MessageID)ID_SESSION_CONFIG_REJECTED); + if (reason && reasonLength > 0) + bitStream.Write(reason, reasonLength); + SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, remoteSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); + + // The refusal has to reach the client before the socket goes away, so drop on no-ack rather than + // silently: the client turns ID_SESSION_CONFIG_REJECTED into ID_CONNECTION_ATTEMPT_FAILED itself. + remoteSystem->connectMode = RemoteSystemStruct::DISCONNECT_ON_NO_ACK; + remoteSystem->sessionConfigStartTime = 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetNumberOfRemoteInitiatedConnections( void ) const +{ + if ( remoteSystemList == 0 || endThreads == true ) + return 0; + + unsigned int numberOfIncomingConnections; + numberOfIncomingConnections = 0; + unsigned int i; + for (i=0; i < activeSystemListSize; i++) + { + if ((activeSystemList[i])->isActive && + (activeSystemList[i])->connectMode==RakPeer::RemoteSystemStruct::CONNECTED && + (activeSystemList[i])->weInitiatedTheConnection==false + ) + { + numberOfIncomingConnections++; + } + } + return numberOfIncomingConnections; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct * RakPeer::AssignSystemAddressToRemoteSystemList( const SystemAddress systemAddress, RemoteSystemStruct::ConnectMode connectionMode, RakNetSocket2* incomingRakNetSocket, bool *thisIPConnectedRecently, SystemAddress bindingAddress, int incomingMTU, RakNetGUID guid, bool useSecurity ) +{ + RemoteSystemStruct * remoteSystem; + unsigned i,j,assignedIndex; + MafiaNet::TimeMS time = MafiaNet::GetTimeMS(); +#ifdef _DEBUG + RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); +#endif + + if (limitConnectionFrequencyFromTheSameIP) + { + if (IsLoopbackAddress(systemAddress,false)==false) + { + for ( i = 0; i < maximumNumberOfPeers; i++ ) + { + if ( remoteSystemList[ i ].isActive==true && + remoteSystemList[ i ].systemAddress.EqualsExcludingPort(systemAddress) && + time >= remoteSystemList[ i ].connectionTime && + time - remoteSystemList[ i ].connectionTime < 100 + ) + { + // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots + // Ignore connection attempts if this IP address connected within the last 100 milliseconds + *thisIPConnectedRecently=true; + ValidateRemoteSystemLookup(); + return 0; + } + } + } + } + + // Don't use a different port than what we received on + bindingAddress.CopyPort(incomingRakNetSocket->GetBoundAddress()); + + *thisIPConnectedRecently=false; + for ( assignedIndex = 0; assignedIndex < maximumNumberOfPeers; assignedIndex++ ) + { + if ( remoteSystemList[ assignedIndex ].isActive==false ) + { + // printf("--- Address %s has become active\n", systemAddress.ToString()); + + remoteSystem=remoteSystemList+assignedIndex; + ReferenceRemoteSystem(systemAddress, assignedIndex); + // Stale reason payload from a prior occupant of this slot must never leak into a new connection. + ClearDisconnectReason(remoteSystem); + // Same for session state: peer.h documents it as cleared on slot reuse, and only the teardown + // paths do it today. Without this, any future path that deactivates a slot without going + // through CloseConnectionInternal2 would hand the previous occupant payload to the new one. + ClearSessionConfig(remoteSystem); + remoteSystem->MTUSize=defaultMTUSize; + remoteSystem->guid=guid; + remoteSystem->isActive = true; // This one line causes future incoming packets to go through the reliability layer + // Reserve this reliability layer for ourselves. + // + // incomingMTU comes off the wire -- ID_OPEN_CONNECTION_REQUEST_2 on the accepting side, + // ID_OPEN_CONNECTION_REPLY_2 on the connecting one -- so it is whatever the remote peer + // said, and must be clamped rather than trusted. MTUSize sizes every datagram the + // reliability layer builds into buffers declared MAXIMUM_MTU_SIZE bytes long, so a + // larger value writes past the end of them. A peer built against a higher cap is enough + // to produce one; a hostile peer can name any uint16. This used to be a bare RakAssert, + // which is compiled out of exactly the builds that ship. + if (incomingMTU > MAXIMUM_MTU_SIZE) + incomingMTU = MAXIMUM_MTU_SIZE; + if (incomingMTU > remoteSystem->MTUSize) + remoteSystem->MTUSize=incomingMTU; + RakAssert(remoteSystem->MTUSize <= MAXIMUM_MTU_SIZE); + remoteSystem->reliabilityLayer.Reset(true, remoteSystem->MTUSize, useSecurity); + remoteSystem->reliabilityLayer.SetSplitMessageProgressInterval(splitMessageProgressInterval); + remoteSystem->reliabilityLayer.SetUnreliableTimeout(unreliableTimeout); + remoteSystem->reliabilityLayer.SetTimeoutTime(defaultTimeoutTime); + AddToActiveSystemList(assignedIndex); + if (incomingRakNetSocket->GetBoundAddress()==bindingAddress) + { + remoteSystem->rakNetSocket=incomingRakNetSocket; + } + else + { + // Originally this searched ipList for the internal address the datagram came in on and + // forced the reply onto it, so a multi-homed machine answers from the same IP. That was + // disabled upstream (06/26/09, unconfirmed report that the Vista firewall blocks the reply + // if we force a binding), which left the search computing an index nobody read. Always + // reply on the socket the datagram came in on. + remoteSystem->rakNetSocket=incomingRakNetSocket; + } + + for ( j = 0; j < (unsigned) PING_TIMES_ARRAY_SIZE; j++ ) + { + remoteSystem->pingAndClockDifferential[ j ].pingTime = 65535; + remoteSystem->pingAndClockDifferential[ j ].clockDifferential = 0; + } + + remoteSystem->connectMode=connectionMode; + remoteSystem->pingAndClockDifferentialWriteIndex = 0; + remoteSystem->lowestPing = 65535; + remoteSystem->nextPingTime = 0; // Ping immediately + remoteSystem->weInitiatedTheConnection = false; + remoteSystem->connectionTime = time; + remoteSystem->myExternalSystemAddress = UNASSIGNED_SYSTEM_ADDRESS; + remoteSystem->lastReliableSend=time; + +#ifdef _DEBUG + int indexLoopupCheck=GetIndexFromSystemAddress( systemAddress, true ); + if ((int) indexLoopupCheck!=(int) assignedIndex) + { + RakAssert((int) indexLoopupCheck==(int) assignedIndex); + } +#endif + + return remoteSystem; + } + } + + return 0; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Adjust the first four bytes (treated as unsigned int) of the pointer +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ShiftIncomingTimestamp( unsigned char *data, const SystemAddress &systemAddress ) const +{ +#ifdef _DEBUG + RakAssert( IsActive() ); + RakAssert( data ); +#endif + + MafiaNet::BitStream timeBS( data, sizeof(MafiaNet::Time), false); + MafiaNet::Time encodedTimestamp; + timeBS.Read(encodedTimestamp); + + encodedTimestamp = encodedTimestamp - GetBestClockDifferential( systemAddress ); + timeBS.SetWriteOffset(0); + timeBS.Write(encodedTimestamp); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm +MafiaNet::Time RakPeer::GetBestClockDifferential( const SystemAddress systemAddress ) const +{ + RemoteSystemStruct *remoteSystem = GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + + if ( remoteSystem == 0 ) + return 0; + + return GetClockDifferentialInt(remoteSystem); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::RemoteSystemLookupHashIndex(const SystemAddress &sa) const +{ + return SystemAddress::ToInteger(sa) % ((unsigned int) maximumNumberOfPeers * REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ReferenceRemoteSystem(const SystemAddress &sa, unsigned int remoteSystemListIndex) +{ +// #ifdef _DEBUG +// for ( unsigned int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) +// { +// if (remoteSystemList[remoteSystemIndex].isActive ) +// { +// unsigned int debugHashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); +// RakAssert(debugHashIndex==remoteSystemIndex); +// } +// } +// #endif + + + SystemAddress oldAddress = remoteSystemList[remoteSystemListIndex].systemAddress; + if (oldAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + // The system might be active if rerouting +// RakAssert(remoteSystemList[remoteSystemListIndex].isActive==false); + + // Remove the reference if the reference is pointing to this inactive system + if (GetRemoteSystem(oldAddress)==&remoteSystemList[remoteSystemListIndex]) + DereferenceRemoteSystem(oldAddress); + } + DereferenceRemoteSystem(sa); + +// #ifdef _DEBUG +// for ( unsigned int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) +// { +// if (remoteSystemList[remoteSystemIndex].isActive ) +// { +// unsigned int debugHashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); +// if (debugHashIndex!=remoteSystemIndex) +// { +// RakAssert(debugHashIndex==remoteSystemIndex); +// } +// } +// } +// #endif + + + remoteSystemList[remoteSystemListIndex].systemAddress=sa; + + unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); + RemoteSystemIndex *rsi; + rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); + if (remoteSystemLookup[hashIndex]==0) + { + rsi->next=0; + rsi->index=remoteSystemListIndex; + remoteSystemLookup[hashIndex]=rsi; + } + else + { + RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; + while (cur->next!=0) + { + cur=cur->next; + } + + rsi = remoteSystemIndexPool.Allocate(_FILE_AND_LINE_); + rsi->next=0; + rsi->index=remoteSystemListIndex; + cur->next=rsi; + } + +// #ifdef _DEBUG +// for ( unsigned int remoteSystemIndex = 0; remoteSystemIndex < maximumNumberOfPeers; ++remoteSystemIndex ) +// { +// if (remoteSystemList[remoteSystemIndex].isActive ) +// { +// unsigned int debugHashIndex = GetRemoteSystemIndex(remoteSystemList[remoteSystemIndex].systemAddress); +// RakAssert(debugHashIndex==remoteSystemIndex); +// } +// } +// #endif + + + RakAssert(GetRemoteSystemIndex(sa)==remoteSystemListIndex); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DereferenceRemoteSystem(const SystemAddress &sa) +{ + unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); + RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; + RemoteSystemIndex *last = 0; + while (cur!=0) + { + if (remoteSystemList[cur->index].systemAddress==sa) + { + if (last==0) + { + remoteSystemLookup[hashIndex]=cur->next; + } + else + { + last->next=cur->next; + } + remoteSystemIndexPool.Release(cur,_FILE_AND_LINE_); + break; + } + last=cur; + cur=cur->next; + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetRemoteSystemIndex(const SystemAddress &sa) const +{ + unsigned int hashIndex = RemoteSystemLookupHashIndex(sa); + RemoteSystemIndex *cur = remoteSystemLookup[hashIndex]; + while (cur!=0) + { + if (remoteSystemList[cur->index].systemAddress==sa) + return cur->index; + cur=cur->next; + } + return (unsigned int) -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RakPeer::RemoteSystemStruct* RakPeer::GetRemoteSystem(const SystemAddress &sa) const +{ + unsigned int remoteSystemIndex = GetRemoteSystemIndex(sa); + if (remoteSystemIndex==(unsigned int)-1) + return 0; + return remoteSystemList + remoteSystemIndex; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearRemoteSystemLookup(void) +{ + remoteSystemIndexPool.Clear(_FILE_AND_LINE_); + MafiaNet::OP_DELETE_ARRAY(remoteSystemLookup,_FILE_AND_LINE_); + remoteSystemLookup=0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::AddToActiveSystemList(unsigned int remoteSystemListIndex) +{ + activeSystemList[activeSystemListSize++]=remoteSystemList+remoteSystemListIndex; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::RemoveFromActiveSystemList(const SystemAddress &sa) +{ + unsigned int i; + for (i=0; i < activeSystemListSize; i++) + { + RemoteSystemStruct *rss=activeSystemList[i]; + if (rss->systemAddress==sa) + { + activeSystemList[i]=activeSystemList[activeSystemListSize-1]; + activeSystemListSize--; + return; + } + } + RakAssert("activeSystemList invalid, entry not found in RemoveFromActiveSystemList. Ensure that AddToActiveSystemList and RemoveFromActiveSystemList are called by the same thread." && 0); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +/* +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::LookupIndexUsingHashIndex(const SystemAddress &sa) const +{ + unsigned int scanCount=0; + unsigned int index = RemoteSystemLookupHashIndex(sa); + if (remoteSystemLookup[index].index==(unsigned int)-1) + return (unsigned int) -1; + while (remoteSystemList[remoteSystemLookup[index].index].systemAddress!=sa) + { + if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) + index=0; + if (++scanCount>(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) + return (unsigned int) -1; + if (remoteSystemLookup[index].index==-1) + return (unsigned int) -1; + } + return index; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::RemoteSystemListIndexUsingHashIndex(const SystemAddress &sa) const +{ + unsigned int index = LookupIndexUsingHashIndex(sa); + if (index!=(unsigned int) -1) + { + return remoteSystemLookup[index].index; + } + return (unsigned int) -1; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::FirstFreeRemoteSystemLookupIndex(const SystemAddress &sa) const +{ +// unsigned int collisionCount=0; + unsigned int index = RemoteSystemLookupHashIndex(sa); + while (remoteSystemLookup[index].index!=(unsigned int)-1) + { + if (++index==(unsigned int) maximumNumberOfPeers*REMOTE_SYSTEM_LOOKUP_HASH_MULTIPLE) + index=0; +// collisionCount++; + } +// printf("%i collisions. Using index %i\n", collisionCount, index); + return index; +} +*/ +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::IsLoopbackAddress(const AddressOrGUID &systemIdentifier, bool matchPort) const +{ + if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) + return systemIdentifier.rakNetGuid==myGuid; + + for (int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS && ipList[i]!=UNASSIGNED_SYSTEM_ADDRESS; i++) + { + if (matchPort) + { + if (ipList[i]==systemIdentifier.systemAddress) + return true; + } + else + { + if (ipList[i].EqualsExcludingPort(systemIdentifier.systemAddress)) + return true; + } + } + + return (matchPort==true && systemIdentifier.systemAddress==firstExternalID) || + (matchPort==false && systemIdentifier.systemAddress.EqualsExcludingPort(firstExternalID)); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +SystemAddress RakPeer::GetLoopbackAddress(void) const +{ + + return ipList[0]; + + + +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::AllowIncomingConnections(void) const +{ + // Counts peers still running the session handshake as well as fully established ones. + // GetNumberOfRemoteInitiatedConnections() deliberately reports only CONNECTED peers -- that is what + // an application means by "players" -- but admission control must not share that view: a peer parked + // in EXCHANGING_SESSION_DATA already owns a slot, and counting only CONNECTED would let peers that + // stall the handshake push the real total past SetMaximumIncomingConnections(). + if ( remoteSystemList == 0 || endThreads == true ) + return false; + + return AllowIncomingConnection(UNASSIGNED_SYSTEM_ADDRESS); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::AllowIncomingConnection(const SystemAddress &address) const +{ + if ( remoteSystemList == 0 || endThreads == true ) + return false; + + SessionAdmission::Limits limits; + limits.maxIncoming = GetMaximumIncomingConnections(); + limits.maxPending = maximumPendingSessions; + limits.maxPendingPerAddress = maximumPendingSessionsPerAddress; + + SessionAdmission::Counts counts; + CountRemoteInitiatedPeers(address, counts.connected, counts.exchanging, counts.pending, counts.pendingFromAddress); + return SessionAdmission::MayStartConnecting(limits, counts); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// A pending peer is one that asked to connect and is not CONNECTED yet: still proving the transport +// (UNVERIFIED_SENDER, HANDLING_CONNECTION_REQUEST) or in the session handshake. A peer being torn down is +// in none of these, so a refusal frees its share of the pool at once rather than after the ack. +void RakPeer::CountRemoteInitiatedPeers(const SystemAddress &address, unsigned int &connected, unsigned int &exchanging, unsigned int &pending, unsigned int &pendingFromAddress) const +{ + connected = exchanging = pending = pendingFromAddress = 0; + for (unsigned int i=0; i < activeSystemListSize; i++) + { + const RemoteSystemStruct *system = activeSystemList[i]; + if (!system->isActive || system->weInitiatedTheConnection) + continue; + + switch (system->connectMode) + { + case RemoteSystemStruct::CONNECTED: + connected++; + break; + case RemoteSystemStruct::EXCHANGING_SESSION_DATA: + exchanging++; + // A peer in the handshake is pending too. + [[fallthrough]]; + case RemoteSystemStruct::UNVERIFIED_SENDER: + case RemoteSystemStruct::HANDLING_CONNECTION_REQUEST: + pending++; + if (address!=UNASSIGNED_SYSTEM_ADDRESS && system->systemAddress.EqualsExcludingPort(address)) + pendingFromAddress++; + break; + default: + break; + } + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +MafiaNet::TimeMS RakPeer::GetSessionTimeoutFor(RemoteSystemStruct *remoteSystem) const +{ + return sessionTimeout != 0 ? sessionTimeout : remoteSystem->reliabilityLayer.GetTimeoutTime(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DeallocRNS2RecvStruct(RNS2RecvStruct *s, const char *file, unsigned int line) +{ + bufferedPacketsFreePoolMutex.Lock(); + bufferedPacketsFreePool.Push(s, file, line); + bufferedPacketsFreePoolMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RNS2RecvStruct *RakPeer::AllocRNS2RecvStruct(const char *file, unsigned int line) +{ + bufferedPacketsFreePoolMutex.Lock(); + if (bufferedPacketsFreePool.Size()>0) + { + RNS2RecvStruct *s = bufferedPacketsFreePool.Pop(); + bufferedPacketsFreePoolMutex.Unlock(); + return s; + } + else + { + bufferedPacketsFreePoolMutex.Unlock(); + return MafiaNet::OP_NEW(file,line); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearBufferedPackets(void) +{ + bufferedPacketsFreePoolMutex.Lock(); + while (bufferedPacketsFreePool.Size()>0) + MafiaNet::OP_DELETE(bufferedPacketsFreePool.Pop(), _FILE_AND_LINE_); + bufferedPacketsFreePoolMutex.Unlock(); + + bufferedPacketsQueueMutex.Lock(); + while (bufferedPacketsQueue.Size()>0) + MafiaNet::OP_DELETE(bufferedPacketsQueue.Pop(), _FILE_AND_LINE_); + bufferedPacketsQueueMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SetupBufferedPackets(void) +{ +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::PushBufferedPacket(RNS2RecvStruct * p) +{ + bufferedPacketsQueueMutex.Lock(); + bufferedPacketsQueue.Push(p, _FILE_AND_LINE_); + bufferedPacketsQueueMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +RNS2RecvStruct *RakPeer::PopBufferedPacket(void) +{ + bufferedPacketsQueueMutex.Lock(); + if (bufferedPacketsQueue.Size()>0) + { + RNS2RecvStruct *s = bufferedPacketsQueue.Pop(); + bufferedPacketsQueueMutex.Unlock(); + return s; + } + bufferedPacketsQueueMutex.Unlock(); + return 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::PingInternal( const SystemAddress target, bool performImmediate, MafiaNet::Reliability reliability ) +{ + if ( IsActive() == false ) + return ; + + MafiaNet::BitStream bitStream(sizeof(unsigned char)+sizeof(MafiaNet::Time)); + bitStream.Write((MessageID)ID_CONNECTED_PING); + bitStream.Write(MafiaNet::GetTime()); + if (performImmediate) + SendImmediate( (char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, reliability, 0, target, false, false, MafiaNet::GetTimeUS(), 0 ); + else + Send( &bitStream, MafiaNet::Priority::Immediate, reliability, 0, target, false ); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::CloseConnectionInternal( const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority ) +{ + RakAssert(orderingChannel < 32); + + if (systemIdentifier.IsUndefined()) + return; + + if (remoteSystemList == 0 || endThreads == true) + return; + + // #med - this is flawed and needs to be resolved properly - socketList[0] is not correct here + CloseConnectionInternal2(systemIdentifier, sendDisconnectionNotification, performImmediate, orderingChannel, disconnectionNotificationPriority, *(socketList[0])); +} + +// #med - better integrate directly in CloseConnectionInternal2() +void RakPeer::CloseConnectionInternal2(const AddressOrGUID& systemIdentifier, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel, MafiaNet::Priority disconnectionNotificationPriority, RakNetSocket2& socket, const MafiaNet::BitStream *reasonData) +{ + RakAssert(orderingChannel < 32); + + if (systemIdentifier.IsUndefined()) + return; + + if ( remoteSystemList == 0 || endThreads == true ) + return; + + SystemAddress target; + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + target=systemIdentifier.systemAddress; + } + else + { + target=GetSystemAddressFromGuid(systemIdentifier.rakNetGuid); + } + + if (target != UNASSIGNED_SYSTEM_ADDRESS && performImmediate) { + target.FixForIPVersion(socket.GetBoundAddress()); + } + + if (sendDisconnectionNotification) + { + NotifyAndFlagForShutdown(target, performImmediate, orderingChannel, disconnectionNotificationPriority, reasonData); + } + else + { + if (performImmediate) + { + unsigned int index = GetRemoteSystemIndex(target); + if (index!=(unsigned int) -1) + { + if ( remoteSystemList[index].isActive ) + { + RemoveFromActiveSystemList(target); + + // Found the index to stop + // printf("--- Address %s has become inactive\n", remoteSystemList[index].systemAddress.ToString()); + remoteSystemList[index].isActive = false; + + remoteSystemList[index].guid=UNASSIGNED_RAKNET_GUID; + + // Reserve this reliability layer for ourselves + //remoteSystemList[ remoteSystemLookup[index].index ].systemAddress = UNASSIGNED_SYSTEM_ADDRESS; + + // Clear any remaining messages + RakAssert(remoteSystemList[index].MTUSize <= MAXIMUM_MTU_SIZE); + remoteSystemList[index].reliabilityLayer.Reset(false, remoteSystemList[index].MTUSize, false); + + // Free any stashed disconnect-reason payload (e.g. delivered already, or never consumed) + ClearDisconnectReason(&remoteSystemList[index]); + + // Free any session-handshake state left over from an exchange that never completed + ClearSessionConfig(&remoteSystemList[index]); + + // Not using this socket + remoteSystemList[index].rakNetSocket = 0; + } + } + } + else + { + BufferedCommandStruct *bcs; + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->command=BufferedCommandStruct::BCS_CLOSE_CONNECTION; + bcs->systemIdentifier=target; + // #med - review whether this is safe here - can't the socket be closed/released before its used? + bcs->socket = &socket; + bcs->data=0; + bcs->orderingChannel=orderingChannel; + bcs->priority=disconnectionNotificationPriority; + bufferedCommands.Push(bcs); + } + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendBuffered( const char *data, BitSize_t numberOfBitsToSend, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) +{ + BufferedCommandStruct *bcs; + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->data = (char*) rakMalloc_Ex( (size_t) BITS_TO_BYTES(numberOfBitsToSend), _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy + if (bcs->data==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); + return; + } + + RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); + RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); + RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); + + memcpy(bcs->data, data, (size_t) BITS_TO_BYTES(numberOfBitsToSend)); + bcs->numberOfBitsToSend=numberOfBitsToSend; + bcs->priority=priority; + bcs->reliability=reliability; + bcs->orderingChannel=orderingChannel; + bcs->systemIdentifier=systemIdentifier; + bcs->broadcast=broadcast; + bcs->connectionMode=connectionMode; + bcs->receipt=receipt; + bcs->command=BufferedCommandStruct::BCS_SEND; + bufferedCommands.Push(bcs); + + if (priority==MafiaNet::Priority::Immediate) + { + // Forces pending sends to go out now, rather than waiting to the next update interval + quitAndDataEvents.SetEvent(); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::SendBufferedList( const char **data, const int *lengths, const int numParameters, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode, uint32_t receipt ) +{ + BufferedCommandStruct *bcs; + unsigned int totalLength=0; + unsigned int lengthOffset; + int i; + for (i=0; i < numParameters; i++) + { + if (lengths[i]>0) + totalLength+=lengths[i]; + } + if (totalLength==0) + return; + + char *dataAggregate; + dataAggregate = (char*) rakMalloc_Ex( (size_t) totalLength, _FILE_AND_LINE_ ); // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy + if (dataAggregate==0) + { + notifyOutOfMemory(_FILE_AND_LINE_); + return; + } + for (i=0, lengthOffset=0; i < numParameters; i++) + { + if (lengths[i]>0) + { + memcpy(dataAggregate+lengthOffset, data[i], lengths[i]); + lengthOffset+=lengths[i]; + } + } + + if (broadcast==false && IsLoopbackAddress(systemIdentifier,true)) + { + SendLoopback(dataAggregate,totalLength); + rakFree_Ex(dataAggregate,_FILE_AND_LINE_); + return; + } + + RakAssert( !( (unsigned int)reliability >= MafiaNet::NUMBER_OF_RELIABILITIES || (int)reliability < 0 ) ); + RakAssert( !( (int)priority > (int)MafiaNet::NUMBER_OF_PRIORITIES || (int)priority < 0 ) ); + RakAssert( !( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); + + bcs=bufferedCommands.Allocate( _FILE_AND_LINE_ ); + bcs->data = dataAggregate; + bcs->numberOfBitsToSend=BYTES_TO_BITS(totalLength); + bcs->priority=priority; + bcs->reliability=reliability; + bcs->orderingChannel=orderingChannel; + bcs->systemIdentifier=systemIdentifier; + bcs->broadcast=broadcast; + bcs->connectionMode=connectionMode; + bcs->receipt=receipt; + bcs->command=BufferedCommandStruct::BCS_SEND; + bufferedCommands.Push(bcs); + + if (priority==MafiaNet::Priority::Immediate) + { + // Forces pending sends to go out now, rather than waiting to the next update interval + quitAndDataEvents.SetEvent(); + } +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::SendImmediate( char *data, BitSize_t numberOfBitsToSend, MafiaNet::Priority priority, MafiaNet::Reliability reliability, char orderingChannel, const AddressOrGUID systemIdentifier, bool broadcast, bool useCallerDataAllocation, MafiaNet::TimeUS currentTime, uint32_t receipt ) +{ + unsigned *sendList; + unsigned sendListSize; + bool callerDataAllocationUsed; + unsigned int remoteSystemIndex, sendListIndex; // Iterates into the list of remote systems +// unsigned numberOfBytesUsed = (unsigned) BITS_TO_BYTES(numberOfBitsToSend); + callerDataAllocationUsed=false; + + sendListSize=0; + + if (systemIdentifier.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + remoteSystemIndex=GetIndexFromSystemAddress( systemIdentifier.systemAddress, true ); + else if (systemIdentifier.rakNetGuid!=UNASSIGNED_RAKNET_GUID) + remoteSystemIndex=GetSystemIndexFromGuid(systemIdentifier.rakNetGuid); + else + remoteSystemIndex=(unsigned int) -1; + + // 03/06/06 - If broadcast is false, use the optimized version of GetIndexFromSystemAddress + if (broadcast==false) + { + if (remoteSystemIndex==(unsigned int) -1) + { +#ifdef _DEBUG +// int debugIndex = GetRemoteSystemIndex(systemIdentifier.systemAddress); +#endif + return false; + } + + #if USE_ALLOCA==1 + sendList=(unsigned *)alloca(sizeof(unsigned)); + #else + sendList = (unsigned *) rakMalloc_Ex(sizeof(unsigned), _FILE_AND_LINE_); + #endif + + if (remoteSystemList[remoteSystemIndex].isActive && + remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP && + remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY && + remoteSystemList[remoteSystemIndex].connectMode!=RemoteSystemStruct::DISCONNECT_ON_NO_ACK) + { + sendList[0]=remoteSystemIndex; + sendListSize=1; + } + } + else + { + #if USE_ALLOCA==1 + sendList=(unsigned *)alloca(sizeof(unsigned)*maximumNumberOfPeers); + #else + sendList = (unsigned *) rakMalloc_Ex(sizeof(unsigned)*maximumNumberOfPeers, _FILE_AND_LINE_); + #endif + + // remoteSystemList in network thread + unsigned int idx; + for ( idx = 0; idx < maximumNumberOfPeers; idx++ ) + { + if (remoteSystemIndex!=(unsigned int) -1 && idx==remoteSystemIndex) + continue; + + // Peers still running the session handshake are excluded: the application has not been told they + // exist, so broadcasting to them would hand application data to a peer it never accepted -- and + // possibly to one it is about to refuse with RejectSession(). Internal handshake traffic is always + // directed, so it never reaches this branch. + if ( remoteSystemList[ idx ].isActive && remoteSystemList[ idx ].systemAddress != UNASSIGNED_SYSTEM_ADDRESS && + remoteSystemList[ idx ].connectMode != RemoteSystemStruct::EXCHANGING_SESSION_DATA ) + sendList[sendListSize++]=idx; + } + } + + if (sendListSize==0) + { + #if !defined(USE_ALLOCA) + rakFree_Ex(sendList, _FILE_AND_LINE_ ); + #endif + + return false; + } + + for (sendListIndex=0; sendListIndex < sendListSize; sendListIndex++) + { + // Send may split the packet and thus deallocate data. Don't assume data is valid if we use the callerAllocationData + bool useData = useCallerDataAllocation && callerDataAllocationUsed==false && sendListIndex+1==sendListSize; + remoteSystemList[sendList[sendListIndex]].reliabilityLayer.Send( data, numberOfBitsToSend, priority, reliability, orderingChannel, useData==false, remoteSystemList[sendList[sendListIndex]].MTUSize, currentTime, receipt ); + if (useData) + callerDataAllocationUsed=true; + + if (reliability==MafiaNet::Reliability::Reliable || + reliability==MafiaNet::Reliability::ReliableOrdered || + reliability==MafiaNet::Reliability::ReliableSequenced || + reliability==MafiaNet::Reliability::ReliableWithAckReceipt || + reliability==MafiaNet::Reliability::ReliableOrderedWithAckReceipt +// || +// reliability==RELIABLE_SEQUENCED_WITH_ACK_RECEIPT + ) + remoteSystemList[sendList[sendListIndex]].lastReliableSend=(MafiaNet::TimeMS)(currentTime/(MafiaNet::TimeUS)1000); + } + +#if !defined(USE_ALLOCA) + rakFree_Ex(sendList, _FILE_AND_LINE_ ); +#endif + + // Return value only meaningful if true was passed for useCallerDataAllocation. Means the reliability layer used that data copy, so the caller should not deallocate it + return callerDataAllocationUsed; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ResetSendReceipt(void) +{ + sendReceiptSerialMutex.Lock(); + sendReceiptSerial=1; + sendReceiptSerialMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::OnConnectedPong(MafiaNet::Time sendPingTime, MafiaNet::Time sendPongTime, RemoteSystemStruct *remoteSystem) +{ + MafiaNet::Time ping; +// MafiaNet::TimeMS lastPing; + MafiaNet::Time time = MafiaNet::GetTime(); // Update the time value to be accurate + if (time > sendPingTime) + ping = time - sendPingTime; + else + ping=0; + +// lastPing = remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime; + + remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime = ( unsigned short ) ping; + // Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm + // Divide each integer by 2, rather than the sum by 2, to prevent overflow + remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].clockDifferential = sendPongTime - ( time/2 + sendPingTime/2 ); + + if ( remoteSystem->lowestPing == (unsigned short)-1 || remoteSystem->lowestPing > (int) ping ) + remoteSystem->lowestPing = (unsigned short) ping; + + if ( ++( remoteSystem->pingAndClockDifferentialWriteIndex ) == (MafiaNet::Time) PING_TIMES_ARRAY_SIZE ) + remoteSystem->pingAndClockDifferentialWriteIndex = 0; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearBufferedCommands(void) +{ + BufferedCommandStruct *bcs; + + while ((bcs=bufferedCommands.Pop())!=0) + { + if (bcs->data) + rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); + + bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); + } + bufferedCommands.Clear(_FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearSocketQueryOutput(void) +{ + socketQueryOutput.Clear(_FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::ClearRequestedConnectionList(void) +{ + DataStructures::Queue freeQueue; + requestedConnectionQueueMutex.Lock(); + while (requestedConnectionQueue.Size()) + freeQueue.Push(requestedConnectionQueue.Pop(), _FILE_AND_LINE_ ); + requestedConnectionQueueMutex.Unlock(); + unsigned i; + for (i=0; i < freeQueue.Size(); i++) + { +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: In ClearRequestedConnectionList(), Deleting freeQueue index %i client_handshake %x\n", i, freeQueue[i]->client_handshake); + MafiaNet::OP_DELETE(freeQueue[i]->client_handshake,_FILE_AND_LINE_); +#endif + MafiaNet::OP_DELETE(freeQueue[i], _FILE_AND_LINE_ ); + } +} +bool RakPeer::IsInsideNetworkThreadReceive(void) const +{ + return networkThreadDispatcher.load()==std::this_thread::get_id(); +} + +void RakPeer::SetNetworkThreadReceiveStopped(bool stopped) +{ + pluginListNetworkThreadMutex.Lock(); + networkThreadReceiveStopped = stopped; + pluginListNetworkThreadMutex.Unlock(); +} + +bool RakPeer::DispatchNetworkThreadReceive(Packet *packet) +{ + pluginListNetworkThreadMutex.Lock(); + if (networkThreadReceiveStopped) + { + pluginListNetworkThreadMutex.Unlock(); + return false; + } + + networkThreadDispatcher = std::this_thread::get_id(); + PluginReceiveResult result = RR_CONTINUE_PROCESSING; + for (unsigned int i=0; i < pluginListNetworkThread.Size() && result==RR_CONTINUE_PROCESSING; i++) + result = pluginListNetworkThread[i]->OnReceiveNetworkThread(packet); + networkThreadDispatcher = std::thread::id(); + pluginListNetworkThreadMutex.Unlock(); + + if (result==RR_STOP_PROCESSING_AND_DEALLOCATE) + DeallocatePacket(packet); + return result!=RR_CONTINUE_PROCESSING; +} + +inline void RakPeer::AddPacketToProducer(MafiaNet::Packet *p) +{ + packetReturnMutex.Lock(); + packetReturnQueue.Push(p,_FILE_AND_LINE_); + packetReturnMutex.Unlock(); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +union Buff6AndBuff8 +{ + unsigned char buff6[6]; + uint64_t buff8; +}; +uint64_t RakPeerInterface::Get64BitUniqueRandomNumber(void) +{ + // Mac address is a poor solution because you can't have multiple connections from the same system + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#if defined(_WIN32) + uint64_t g= MafiaNet::GetTimeUS(); + + MafiaNet::TimeUS lastTime, thisTime; + int j; + // Sleep a small random time, then use the last 4 bits as a source of randomness + for (j=0; j < 8; j++) + { + lastTime = MafiaNet::GetTimeUS(); + RakSleep(1); + RakSleep(0); + thisTime = MafiaNet::GetTimeUS(); + MafiaNet::TimeUS diff = thisTime-lastTime; + unsigned int diff4Bits = (unsigned int) (diff & 15); + diff4Bits <<= 32-4; + diff4Bits >>= j*4; + ((char*)&g)[j] ^= diff4Bits; + } + // Timer jitter alone collides when peers are created back to back, and is + // constant under a test's virtual clock. Mix in the same entropy as below. + static std::atomic sequence {0}; + std::random_device entropy; + g ^= ((uint64_t)entropy() << 32) ^ (uint64_t)entropy(); + g ^= (sequence.fetch_add(1, std::memory_order_relaxed) + 1) * 0x9E3779B97F4A7C15ull; + if (g == 0 || g == (uint64_t)-1) + g = 1; + return g; + +#else + // The time alone is not unique: two peers constructed within the same microsecond -- a server + // and a client created back to back in one process -- got the same GUID, and a peer then + // resolved the other's GUID to itself, so CloseConnection() and friends silently did nothing. + // Mix in the OS entropy source and a per-process counter. + struct timeval tv; + gettimeofday(&tv, nullptr); + uint64_t g = (uint64_t)tv.tv_usec + (uint64_t)tv.tv_sec * 1000000; + + static std::atomic sequence {0}; + std::random_device entropy; + g ^= ((uint64_t)entropy() << 32) ^ (uint64_t)entropy(); + g ^= (sequence.fetch_add(1, std::memory_order_relaxed) + 1) * 0x9E3779B97F4A7C15ull; + + // Neither value is a usable GUID: 0 means "not generated" and all-ones is UNASSIGNED_RAKNET_GUID. + if (g == 0 || g == (uint64_t)-1) + g = 1; + return g; +#endif +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::GenerateGUID(void) +{ + myGuid.g=Get64BitUniqueRandomNumber(); + +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// void MafiaNet::ProcessPortUnreachable( SystemAddress systemAddress, RakPeer *rakPeer ) +// { +// (void) binaryAddress; +// (void) port; +// (void) rakPeer; +// +// } +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +namespace MafiaNet { +bool ProcessOfflineNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSocket2* rakNetSocket, bool *isOfflineMessage, MafiaNet::TimeUS timeRead ) +{ + (void) timeRead; + RakPeer::RemoteSystemStruct *remoteSystem; + MafiaNet::Packet *packet; + unsigned i; + + + char str1[64]; + systemAddress.ToString(false, str1,static_cast(64)); + if (rakPeer->IsBanned( str1 )) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + MafiaNet::BitStream bs; + bs.Write((MessageID)ID_CONNECTION_BANNED); + bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + + + RNS2_SendParameters bsp; + bsp.data = (char*) bs.GetData(); + bsp.length = bs.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*) bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + +/* + unsigned i; + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*) bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); + SocketLayer::SendTo( rakNetSocket, (char*) bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + */ + + return true; + } + + + + // The reason for all this is that the reliability layer has no way to tell between offline messages that arrived late for a player that is now connected, + // and a regular encoding. So I insert OFFLINE_MESSAGE_DATA_ID into the stream, the encoding of which is essentially impossible to hit by chance + if (length <=2) + { + *isOfflineMessage=true; + } + else if ( + ((unsigned char)data[0] == ID_UNCONNECTED_PING || + (unsigned char)data[0] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS) && + (size_t) length >= sizeof(unsigned char) + sizeof(MafiaNet::Time) + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(MafiaNet::Time), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if ((unsigned char)data[0] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char) + sizeof(MafiaNet::TimeMS) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(unsigned char) + sizeof(MafiaNet::Time) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if ( + (unsigned char)data[0] == ID_OUT_OF_BAND_INTERNAL && + (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(MessageID) + RakNetGUID::size(), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if ( + ( + (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_1 || + (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY_2 || + (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_1 || + (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST_2 || + (unsigned char)data[0] == ID_CONNECTION_ATTEMPT_FAILED || + (unsigned char)data[0] == ID_NO_FREE_INCOMING_CONNECTIONS || + (unsigned char)data[0] == ID_CONNECTION_BANNED || + (unsigned char)data[0] == ID_ALREADY_CONNECTED || + (unsigned char)data[0] == ID_IP_RECENTLY_CONNECTED) && + (size_t) length >= sizeof(MessageID) + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID)) + { + *isOfflineMessage=memcmp(data+sizeof(MessageID), OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else if (((unsigned char)data[0] == ID_INCOMPATIBLE_PROTOCOL_VERSION&& + (size_t) length == sizeof(MessageID)*2 + RakNetGUID::size() + sizeof(OFFLINE_MESSAGE_DATA_ID))) + { + *isOfflineMessage=memcmp(data+sizeof(MessageID)*2, OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID))==0; + } + else + { + *isOfflineMessage=false; + } + + if (*isOfflineMessage) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + // These are all messages from unconnected systems. Messages here can be any size, but are never processed from connected systems. + if ( ( (unsigned char) data[ 0 ] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS + || (unsigned char)(data)[0] == ID_UNCONNECTED_PING) && (size_t) length >= sizeof(unsigned char)+sizeof(MafiaNet::Time)+sizeof(OFFLINE_MESSAGE_DATA_ID) ) + { + if ( (unsigned char)(data)[0] == ID_UNCONNECTED_PING || + rakPeer->AllowIncomingConnections() ) // Open connections with players + { + MafiaNet::BitStream inBitStream( (unsigned char *) data, length, false ); + inBitStream.IgnoreBits(8); + MafiaNet::Time sendPingTime; + inBitStream.Read(sendPingTime); + inBitStream.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + RakNetGUID remoteGuid=UNASSIGNED_RAKNET_GUID; + inBitStream.Read(remoteGuid); + + MafiaNet::BitStream outBitStream; + outBitStream.Write((MessageID)ID_UNCONNECTED_PONG); // Should be named ID_UNCONNECTED_PONG eventually + outBitStream.Write(sendPingTime); + outBitStream.Write(rakPeer->myGuid); + outBitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + + rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Lock(); + // They are connected, so append offline ping data + outBitStream.Write( (char*)rakPeer->offlinePingResponse.GetData(), rakPeer->offlinePingResponse.GetNumberOfBytesUsed() ); + rakPeer->rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Unlock(); + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*)outBitStream.GetData(), outBitStream.GetNumberOfBytesUsed(), systemAddress); + + RNS2_SendParameters bsp; + bsp.data = (char*) outBitStream.GetData(); + bsp.length = outBitStream.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + + // SocketLayer::SendTo( rakNetSocket, (const char*)outBitStream.GetData(), (unsigned int) outBitStream.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + + packet=rakPeer->AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); + packet->data[0]=data[0]; + packet->systemAddress = systemAddress; + packet->guid=remoteGuid; + packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + rakPeer->AddPacketToProducer(packet); + } + } + // UNCONNECTED MESSAGE Pong with no data. + else if ((unsigned char) data[ 0 ] == ID_UNCONNECTED_PONG && (size_t) length >= sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID) && (size_t) length < sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID)+MAX_OFFLINE_DATA_LENGTH) + { + packet=rakPeer->AllocPacket((unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(MafiaNet::Time)+sizeof(MafiaNet::TimeMS)), _FILE_AND_LINE_); + MafiaNet::BitStream bsIn((unsigned char*) data, length, false); + bsIn.IgnoreBytes(sizeof(unsigned char)); + MafiaNet::Time ping; + bsIn.Read(ping); + bsIn.Read(packet->guid); + + MafiaNet::BitStream bsOut((unsigned char*) packet->data, packet->length, false); + bsOut.ResetWritePointer(); + bsOut.Write((unsigned char)ID_UNCONNECTED_PONG); + MafiaNet::TimeMS pingMS=(MafiaNet::TimeMS)ping; + bsOut.Write(pingMS); + bsOut.WriteAlignedBytes( + (const unsigned char*)data+sizeof(unsigned char)+sizeof(MafiaNet::Time)+RakNetGUID::size()+sizeof(OFFLINE_MESSAGE_DATA_ID), + length-sizeof(unsigned char)-sizeof(MafiaNet::Time)-RakNetGUID::size()-sizeof(OFFLINE_MESSAGE_DATA_ID) + ); + + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + rakPeer->AddPacketToProducer(packet); + } + else if ((unsigned char) data[ 0 ] == ID_OUT_OF_BAND_INTERNAL && + (size_t) length > sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)+RakNetGUID::size() && + (size_t) length < MAX_OFFLINE_DATA_LENGTH+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)+RakNetGUID::size()) + { + unsigned int dataLength = (unsigned int) (length-sizeof(OFFLINE_MESSAGE_DATA_ID)-RakNetGUID::size()-sizeof(MessageID)); + RakAssert(dataLength<1024); + packet=rakPeer->AllocPacket(dataLength+1, _FILE_AND_LINE_); + RakAssert(packet->length<1024); + + MafiaNet::BitStream bs2((unsigned char*) data, length, false); + bs2.IgnoreBytes(sizeof(MessageID)); + bs2.Read(packet->guid); + + if (data[sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size()]==ID_ADVERTISE_SYSTEM) + { + packet->length--; + packet->bitSize=BYTES_TO_BITS(packet->length); + packet->data[0]=ID_ADVERTISE_SYSTEM; + memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID)*2 + RakNetGUID::size(), dataLength-1); + } + else + { + packet->data[0]=ID_OUT_OF_BAND_INTERNAL; + memcpy(packet->data+1, data+sizeof(OFFLINE_MESSAGE_DATA_ID)+sizeof(MessageID) + RakNetGUID::size(), dataLength); + } + + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = ( SystemIndex ) rakPeer->GetIndexFromSystemAddress( systemAddress, true ); + packet->guid.systemIndex=packet->systemAddress.systemIndex; + rakPeer->AddPacketToProducer(packet); + } + else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_1) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + MafiaNet::BitStream bsIn((unsigned char*) data,length,false); + bsIn.IgnoreBytes(sizeof(MessageID)); + bsIn.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + RakNetGUID serverGuid; + bsIn.Read(serverGuid); + unsigned char serverHasSecurity; + uint32_t cookie; + (void) cookie; + bsIn.Read(serverHasSecurity); + // Even if the server has security, it may not be required of us if we are in the security exception list + if (serverHasSecurity) + { + bsIn.Read(cookie); + } + + MafiaNet::BitStream bsOut; + bsOut.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_2); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + if (serverHasSecurity) + bsOut.Write(cookie); + + rakPeer->requestedConnectionQueueMutex.Lock(); + for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) + { + RakPeer::RequestedConnectionStruct *rcs; + rcs=rakPeer->requestedConnectionQueue[i]; + if (rcs->systemAddress==systemAddress) + { + if (serverHasSecurity) + { +#if LIBCAT_SECURITY==1 + unsigned char public_key[cat::EasyHandshake::PUBLIC_KEY_BYTES]; + bsIn.ReadAlignedBytes(public_key, sizeof(public_key)); + + if (rcs->publicKeyMode==PKM_ACCEPT_ANY_PUBLIC_KEY) + { + memcpy(rcs->remote_public_key, public_key, cat::EasyHandshake::PUBLIC_KEY_BYTES); + if (!rcs->client_handshake->Initialize(public_key) || + !rcs->client_handshake->GenerateChallenge(rcs->handshakeChallenge)) + { + CAT_AUDIT_PRINTF("AUDIT: Server passed a bad public key with PKM_ACCEPT_ANY_PUBLIC_KEY"); + rakPeer->requestedConnectionQueueMutex.Unlock(); + return true; + } + } + + if (cat::SecureEqual(public_key, + rcs->remote_public_key, + cat::EasyHandshake::PUBLIC_KEY_BYTES)==false) + { + rakPeer->requestedConnectionQueueMutex.Unlock(); + CAT_AUDIT_PRINTF("AUDIT: Expected public key does not match what was sent by server -- Reporting back ID_PUBLIC_KEY_MISMATCH to user\n"); + + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_PUBLIC_KEY_MISMATCH; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=serverGuid; + rakPeer->AddPacketToProducer(packet); + return true; + } + + if (rcs->client_handshake==0) + { + // Message does not contain a challenge + // We might still pass if we are in the security exception list + bsOut.Write((unsigned char)0); + } + else + { + // Message contains a challenge + bsOut.Write((unsigned char)1); + // challenge + CAT_AUDIT_PRINTF("AUDIT: Sending challenge\n"); + bsOut.WriteAlignedBytes((const unsigned char*) rcs->handshakeChallenge,cat::EasyHandshake::CHALLENGE_BYTES); + } +#else // LIBCAT_SECURITY + // Message does not contain a challenge + bsOut.Write((unsigned char)0); +#endif // LIBCAT_SECURITY + } + else + { + // Server does not need security +#if LIBCAT_SECURITY==1 + if (rcs->client_handshake!=0) + { + rakPeer->requestedConnectionQueueMutex.Unlock(); + CAT_AUDIT_PRINTF("AUDIT: Security disabled by server but we expected security (indicated by client_handshake not null) so failing!\n"); + + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_OUR_SYSTEM_REQUIRES_SECURITY; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=serverGuid; + rakPeer->AddPacketToProducer(packet); + return true; + } +#endif // LIBCAT_SECURITY + + } + + uint16_t mtu; + bsIn.Read(mtu); + + // Binding address + bsOut.Write(rcs->systemAddress); + rakPeer->requestedConnectionQueueMutex.Unlock(); + // MTU + bsOut.Write(mtu); + // Our guid + bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), rcs->systemAddress); + + // SocketLayer::SendTo( rakPeer->socketList[rcs->socketIndex], (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), rcs->systemAddress, _FILE_AND_LINE_ ); + + RNS2_SendParameters bsp; + bsp.data = (char*) bsOut.GetData(); + bsp.length = bsOut.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + + return true; + } + } + rakPeer->requestedConnectionQueueMutex.Unlock(); + } + else if ((unsigned char)(data)[0] == (MessageID)ID_OPEN_CONNECTION_REPLY_2) + { + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + MafiaNet::BitStream bs((unsigned char*) data,length,false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + RakNetGUID guid; + bs.Read(guid); + SystemAddress bindingAddress; + [[maybe_unused]] bool b = bs.Read(bindingAddress); // only checked by RakAssert (debug builds) + RakAssert(b); + uint16_t mtu; + b=bs.Read(mtu); + RakAssert(b); + bool doSecurity=false; + b=bs.Read(doSecurity); + RakAssert(b); + +#if LIBCAT_SECURITY==1 + char answer[cat::EasyHandshake::ANSWER_BYTES]; + CAT_AUDIT_PRINTF("AUDIT: Got ID_OPEN_CONNECTION_REPLY_2 and given doSecurity=%i\n", (int)doSecurity); + if (doSecurity) + { + CAT_AUDIT_PRINTF("AUDIT: Reading cookie and public key\n"); + bs.ReadAlignedBytes((unsigned char*) answer, sizeof(answer)); + } + cat::ClientEasyHandshake *client_handshake=0; +#endif // LIBCAT_SECURITY + + RakPeer::RequestedConnectionStruct *rcs; + bool unlock=true; + rakPeer->requestedConnectionQueueMutex.Lock(); + for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) + { + rcs=rakPeer->requestedConnectionQueue[i]; + + + if (rcs->systemAddress==systemAddress) + { +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: System address matches an entry in the requestedConnectionQueue and doSecurity=%i\n", (int)doSecurity); + if (doSecurity) + { + if (rcs->client_handshake==0) + { + CAT_AUDIT_PRINTF("AUDIT: Server wants security but we didn't set a public key -- Reporting back ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY to user\n"); + rakPeer->requestedConnectionQueueMutex.Unlock(); + + packet=rakPeer->AllocPacket(2, _FILE_AND_LINE_); + packet->data[ 0 ] = ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY; // Attempted a connection and couldn't + packet->data[ 1 ] = 0; // Indicate server public key is missing + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=guid; + rakPeer->AddPacketToProducer(packet); + return true; + } + + CAT_AUDIT_PRINTF("AUDIT: Looks good, preparing to send challenge to server! client_handshake = %x\n", client_handshake); + } + +#endif // LIBCAT_SECURITY + + rakPeer->requestedConnectionQueueMutex.Unlock(); + unlock=false; + + RakAssert(rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT); + // You might get this when already connected because of cross-connections + bool thisIPConnectedRecently=false; + remoteSystem=rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + if (remoteSystem==0) + { + if (rcs->socket == 0) + { + remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); + } + else + { + remoteSystem=rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rcs->socket, &thisIPConnectedRecently, bindingAddress, mtu, guid, doSecurity); + } + } + + // 4/13/09 Attackers can flood ID_OPEN_CONNECTION_REQUEST and use up all available connection slots + // Ignore connection attempts if this IP address connected within the last 100 milliseconds + if (thisIPConnectedRecently==false) + { + // Don't check GetRemoteSystemFromGUID, server will verify + if (remoteSystem) + { + // Move pointer from RequestedConnectionStruct to RemoteSystemStruct +#if LIBCAT_SECURITY==1 + cat::u8 ident[cat::EasyHandshake::IDENTITY_BYTES]; + bool doIdentity = false; + + if (rcs->client_handshake) + { + CAT_AUDIT_PRINTF("AUDIT: Processing answer\n"); + if (rcs->publicKeyMode == PKM_USE_TWO_WAY_AUTHENTICATION) + { + if (!rcs->client_handshake->ProcessAnswerWithIdentity(answer, ident, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) + { + CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); + return true; + } + + doIdentity = true; + } + else + { + if (!rcs->client_handshake->ProcessAnswer(answer, remoteSystem->reliabilityLayer.GetAuthenticatedEncryption())) + { + CAT_AUDIT_PRINTF("AUDIT: Processing answer -- Invalid Answer\n"); + return true; + } + } + CAT_AUDIT_PRINTF("AUDIT: Success!\n"); + + MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); + rcs->client_handshake=0; + } +#endif // LIBCAT_SECURITY + + remoteSystem->weInitiatedTheConnection=true; + remoteSystem->connectMode=RakPeer::RemoteSystemStruct::REQUESTED_CONNECTION; + if (rcs->timeoutTime!=0) + remoteSystem->reliabilityLayer.SetTimeoutTime(rcs->timeoutTime); + + MafiaNet::BitStream temp; + temp.Write( (MessageID)ID_CONNECTION_REQUEST); + temp.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + temp.Write(MafiaNet::GetTime()); + +#if LIBCAT_SECURITY==1 + temp.Write((unsigned char)(doSecurity ? 1 : 0)); + + if (doSecurity) + { + unsigned char proof[32]; + remoteSystem->reliabilityLayer.GetAuthenticatedEncryption()->GenerateProof(proof, sizeof(proof)); + temp.WriteAlignedBytes(proof, sizeof(proof)); + + temp.Write((unsigned char)(doIdentity ? 1 : 0)); + + if (doIdentity) + { + temp.WriteAlignedBytes(ident, sizeof(ident)); + } + } +#else + temp.Write((unsigned char)0); +#endif // LIBCAT_SECURITY + + if ( rcs->outgoingPasswordLength > 0 ) + temp.Write( ( char* ) rcs->outgoingPassword, rcs->outgoingPasswordLength ); + + rakPeer->SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Reliable, 0, systemAddress, false, false, timeRead, 0 ); + } + else + { + // Failed, no connections available anymore + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + packet->guid=guid; + rakPeer->AddPacketToProducer(packet); + } + } + + rakPeer->requestedConnectionQueueMutex.Lock(); + for (unsigned int k=0; k < rakPeer->requestedConnectionQueue.Size(); k++) + { + if (rakPeer->requestedConnectionQueue[k]->systemAddress==systemAddress) + { + rakPeer->requestedConnectionQueue.RemoveAtIndex(k); + break; + } + } + rakPeer->requestedConnectionQueueMutex.Unlock(); + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Deleting client_handshake object %x and rcs->client_handshake object %x\n", client_handshake, rcs->client_handshake); + MafiaNet::OP_DELETE(client_handshake,_FILE_AND_LINE_); + MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); +#endif // LIBCAT_SECURITY + MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); + + break; + } + } + + if (unlock) + rakPeer->requestedConnectionQueueMutex.Unlock(); + + return true; + + } + else if ((unsigned char)(data)[0] == (MessageID)ID_CONNECTION_ATTEMPT_FAILED || + (unsigned char)(data)[0] == (MessageID)ID_NO_FREE_INCOMING_CONNECTIONS || + (unsigned char)(data)[0] == (MessageID)ID_CONNECTION_BANNED || + (unsigned char)(data)[0] == (MessageID)ID_ALREADY_CONNECTED || + (unsigned char)(data)[0] == (MessageID)ID_INVALID_PASSWORD || + (unsigned char)(data)[0] == (MessageID)ID_IP_RECENTLY_CONNECTED || + (unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) + { + + MafiaNet::BitStream bs((unsigned char*) data,length,false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + if ((unsigned char)(data)[0] == (MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION) + bs.IgnoreBytes(sizeof(unsigned char)); + + RakNetGUID guid; + bs.Read(guid); + + RakPeer::RequestedConnectionStruct *rcs; + bool connectionAttemptCancelled=false; + rakPeer->requestedConnectionQueueMutex.Lock(); + for (i=0; i < rakPeer->requestedConnectionQueue.Size(); i++) + { + rcs=rakPeer->requestedConnectionQueue[i]; + if (rcs->actionToTake==RakPeer::RequestedConnectionStruct::CONNECT && rcs->systemAddress==systemAddress) + { + connectionAttemptCancelled=true; + rakPeer->requestedConnectionQueue.RemoveAtIndex(i); + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Connection attempt canceled so deleting rcs->client_handshake object %x\n", rcs->client_handshake); + MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); +#endif // LIBCAT_SECURITY + MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); + break; + } + } + + rakPeer->requestedConnectionQueueMutex.Unlock(); + + if (connectionAttemptCancelled) + { + // Tell user of connection attempt failed + packet=rakPeer->AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = data[0]; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = systemAddress; + packet->guid=guid; + rakPeer->AddPacketToProducer(packet); + } + } + else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_1 && length > (int) (1+sizeof(OFFLINE_MESSAGE_DATA_ID))) + {/* + static int x = 0; + ++x; + + SystemAddress *addr = (SystemAddress*)&systemAddress; + addr->binaryAddress += x;*/ + + //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); + char remoteProtocol=data[1+sizeof(OFFLINE_MESSAGE_DATA_ID)]; + if (remoteProtocol!=RAKNET_PROTOCOL_VERSION) + { + MafiaNet::BitStream bs; + bs.Write((MessageID)ID_INCOMPATIBLE_PROTOCOL_VERSION); + bs.Write((unsigned char)RAKNET_PROTOCOL_VERSION); + bs.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bs.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((char*)bs.GetData(), bs.GetNumberOfBitsUsed(), systemAddress); + + // SocketLayer::SendTo( rakNetSocket, (char*)bs.GetData(), bs.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + + RNS2_SendParameters bsp; + bsp.data = (char*) bs.GetData(); + bsp.length = bs.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + return true; + } + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketReceive(data, length*8, systemAddress); + + MafiaNet::BitStream bsOut; + bsOut.Write((MessageID)ID_OPEN_CONNECTION_REPLY_1); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); +#if LIBCAT_SECURITY==1 + if (rakPeer->_using_security) + { + bsOut.Write((unsigned char) 1); // HasCookie Yes + // Write cookie + uint32_t cookie = rakPeer->_cookie_jar->Generate(&systemAddress.address,sizeof(systemAddress.address)); + CAT_AUDIT_PRINTF("AUDIT: Writing cookie %i to %i:%i\n", cookie, systemAddress); + bsOut.Write(cookie); + // Write my public key + bsOut.WriteAlignedBytes((const unsigned char *) rakPeer->my_public_key,sizeof(rakPeer->my_public_key)); + } + else +#endif // LIBCAT_SECURITY + bsOut.Write((unsigned char) 0); // HasCookie oN + + // MTU. Lower MTU if it is exceeds our own limit + if (length+UDP_HEADER_SIZE > MAXIMUM_MTU_SIZE) + bsOut.WriteCasted(MAXIMUM_MTU_SIZE); + else + bsOut.WriteCasted(length+UDP_HEADER_SIZE); + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + // SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + + RNS2_SendParameters bsp; + bsp.data = (char*) bsOut.GetData(); + bsp.length = bsOut.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + } + else if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST_2) + { + SystemAddress bindingAddress; + RakNetGUID guid; + MafiaNet::BitStream bsOut; + MafiaNet::BitStream bs((unsigned char*) data, length, false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + + bool requiresSecurityOfThisClient=false; +#if LIBCAT_SECURITY==1 + char remoteHandshakeChallenge[cat::EasyHandshake::CHALLENGE_BYTES]; + + if (rakPeer->_using_security) + { + systemAddress.ToString(false, str1, static_cast(64)); + requiresSecurityOfThisClient=rakPeer->IsInSecurityExceptionList(str1)==false; + + uint32_t cookie; + bs.Read(cookie); + CAT_AUDIT_PRINTF("AUDIT: Got cookie %i from %i:%i\n", cookie, systemAddress); + if (rakPeer->_cookie_jar->Verify(&systemAddress.address,sizeof(systemAddress.address), cookie)==false) + { + return true; + } + CAT_AUDIT_PRINTF("AUDIT: Cookie good!\n"); + + unsigned char clientWroteChallenge; + bs.Read(clientWroteChallenge); + + if (requiresSecurityOfThisClient==true && clientWroteChallenge==0) + { + // Fail, client doesn't support security, and it is required + return true; + } + + if (clientWroteChallenge) + { + bs.ReadAlignedBytes((unsigned char*) remoteHandshakeChallenge, cat::EasyHandshake::CHALLENGE_BYTES); +#ifdef CAT_AUDIT + printf("AUDIT: RECV CHALLENGE "); + for (int ii = 0; ii < sizeof(remoteHandshakeChallenge); ++ii) + { + printf("%02x", (cat::u8)remoteHandshakeChallenge[ii]); + } + printf("\n"); +#endif + } + } +#endif // LIBCAT_SECURITY + + bs.Read(bindingAddress); + uint16_t mtu; + bs.Read(mtu); + bs.Read(guid); + + RakPeer::RemoteSystemStruct *rssFromSA = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + bool IPAddrInUse = rssFromSA != 0 && rssFromSA->isActive; + RakPeer::RemoteSystemStruct *rssFromGuid = rakPeer->GetRemoteSystemFromGUID(guid, true); + bool GUIDInUse = rssFromGuid != 0 && rssFromGuid->isActive; + + // IPAddrInUse, GuidInUse, outcome + // TRUE, , TRUE , ID_OPEN_CONNECTION_REPLY if they are the same, else ID_ALREADY_CONNECTED + // FALSE, , TRUE , ID_ALREADY_CONNECTED (someone else took this guid) + // TRUE, , FALSE , ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) + // FALSE , FALSE , Allow connection + + int outcome; + if (IPAddrInUse & GUIDInUse) + { + if (rssFromSA==rssFromGuid && rssFromSA->connectMode==RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER) + { + // ID_OPEN_CONNECTION_REPLY if they are the same + outcome=1; + + // Note to self: If REQUESTED_CONNECTION, this means two systems attempted to connect to each other at the same time, and one finished first. + // Returns ID)_CONNECTION_REQUEST_ACCEPTED to one system, and ID_ALREADY_CONNECTED followed by ID_NEW_INCOMING_CONNECTION to another + } + else + { + // ID_ALREADY_CONNECTED (restarted raknet, connected again from same ip, plus someone else took this guid) + outcome=2; + } + } + else if (IPAddrInUse==false && GUIDInUse==true) + { + // ID_ALREADY_CONNECTED (someone else took this guid) + outcome=3; + } + else if (IPAddrInUse==true && GUIDInUse==false) + { + // ID_ALREADY_CONNECTED (silently disconnected, restarted rakNet) + outcome=4; + } + else + { + // Allow connection + outcome=0; + } + + MafiaNet::BitStream bsAnswer; + bsAnswer.Write((MessageID)ID_OPEN_CONNECTION_REPLY_2); + bsAnswer.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsAnswer.Write(rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)); + bsAnswer.Write(systemAddress); + bsAnswer.Write(mtu); + bsAnswer.Write(requiresSecurityOfThisClient); + + if (outcome==1) + { + // Duplicate connection request packet from packetloss + // Send back the same answer +#if LIBCAT_SECURITY==1 + if (requiresSecurityOfThisClient) + { + CAT_AUDIT_PRINTF("AUDIT: Resending public key and answer from packetloss. Sending ID_OPEN_CONNECTION_REPLY_2\n"); + bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); + } +#endif // LIBCAT_SECURITY + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); + // SocketLayer::SendTo( rakNetSocket, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + + RNS2_SendParameters bsp; + bsp.data = (char*) bsAnswer.GetData(); + bsp.length = bsAnswer.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + + return true; + } + else if (outcome!=0) + { + bsOut.Write((MessageID)ID_ALREADY_CONNECTED); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(rakPeer->myGuid); + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + // SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + RNS2_SendParameters bsp; + bsp.data = (char*) bsOut.GetData(); + bsp.length = bsOut.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + + return true; + } + + if (rakPeer->AllowIncomingConnection(systemAddress)==false) + { + bsOut.Write((MessageID)ID_NO_FREE_INCOMING_CONNECTIONS); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(rakPeer->myGuid); + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + //SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + RNS2_SendParameters bsp; + bsp.data = (char*) bsOut.GetData(); + bsp.length = bsOut.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + + return true; + } + + bool thisIPConnectedRecently=false; + rssFromSA = rakPeer->AssignSystemAddressToRemoteSystemList(systemAddress, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER, rakNetSocket, &thisIPConnectedRecently, bindingAddress, mtu, guid, requiresSecurityOfThisClient); + + if (thisIPConnectedRecently==true) + { + bsOut.Write((MessageID)ID_IP_RECENTLY_CONNECTED); + bsOut.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bsOut.Write(rakPeer->myGuid); + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsOut.GetData(), bsOut.GetNumberOfBitsUsed(), systemAddress); + //SocketLayer::SendTo( rakNetSocket, (const char*) bsOut.GetData(), bsOut.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + + RNS2_SendParameters bsp; + bsp.data = (char*) bsOut.GetData(); + bsp.length = bsOut.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + + return true; + } + +#if LIBCAT_SECURITY==1 + if (requiresSecurityOfThisClient) + { + CAT_AUDIT_PRINTF("AUDIT: Writing public key. Sending ID_OPEN_CONNECTION_REPLY_2\n"); + if (rakPeer->_server_handshake->ProcessChallenge(remoteHandshakeChallenge, rssFromSA->answer, rssFromSA->reliabilityLayer.GetAuthenticatedEncryption() )) + { + CAT_AUDIT_PRINTF("AUDIT: Challenge good!\n"); + // Keep going to OK block + } + else + { + CAT_AUDIT_PRINTF("AUDIT: Challenge BAD!\n"); + + // Unassign this remote system + rakPeer->DereferenceRemoteSystem(systemAddress); + return true; + } + + bsAnswer.WriteAlignedBytes((const unsigned char *) rssFromSA->answer,sizeof(rssFromSA->answer)); + } +#endif // LIBCAT_SECURITY + + for (i=0; i < rakPeer->pluginListNTS.Size(); i++) + rakPeer->pluginListNTS[i]->OnDirectSocketSend((const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBitsUsed(), systemAddress); + // SocketLayer::SendTo( rakNetSocket, (const char*) bsAnswer.GetData(), bsAnswer.GetNumberOfBytesUsed(), systemAddress, _FILE_AND_LINE_ ); + RNS2_SendParameters bsp; + bsp.data = (char*) bsAnswer.GetData(); + bsp.length = bsAnswer.GetNumberOfBytesUsed(); + bsp.systemAddress = systemAddress; + rakNetSocket->Send(&bsp, _FILE_AND_LINE_); + } + return true; + } + + return false; +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// #med - deprecate this overload --- socketList[0] is not really ensured to be the correctly bound socket at all! +void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, MafiaNet::TimeUS timeRead, BitStream &updateBitStream ) +{ + ProcessNetworkPacket(systemAddress,data,length,rakPeer,rakPeer->socketList[0],timeRead, updateBitStream); +} +void ProcessNetworkPacket( SystemAddress systemAddress, const char *data, const int length, RakPeer *rakPeer, RakNetSocket2* rakNetSocket, MafiaNet::TimeUS timeRead, BitStream &updateBitStream ) +{ +#if LIBCAT_SECURITY==1 +#ifdef CAT_AUDIT + printf("AUDIT: RECV "); + for (int ii = 0; ii < length; ++ii) + { + printf("%02x", (cat::u8)data[ii]); + } + printf("\n"); +#endif +#endif // LIBCAT_SECURITY + + RakAssert(systemAddress.GetPort()); + bool isOfflineMessage; + if (ProcessOfflineNetworkPacket(systemAddress, data, length, rakPeer, rakNetSocket, &isOfflineMessage, timeRead)) + { + return; + } + +// MafiaNet::Packet *packet; + RakPeer::RemoteSystemStruct *remoteSystem; + + // See if this datagram came from a connected system + remoteSystem = rakPeer->GetRemoteSystemFromSystemAddress( systemAddress, true, true ); + if ( remoteSystem ) + { + // Handle regular incoming data + // HandleSocketReceiveFromConnectedPlayer is only safe to be called from the same thread as Update, which is this thread + if ( isOfflineMessage==false) + { + remoteSystem->reliabilityLayer.HandleSocketReceiveFromConnectedPlayer( + data, length, systemAddress, rakPeer->pluginListNTS, remoteSystem->MTUSize, + rakNetSocket, &rnr, timeRead, updateBitStream); + } + } + else + { + // int a=5; + // printf("--- Packet from unknown system %s\n", systemAddress.ToString()); + } +} + +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GenerateSeedFromGuid(void) +{ + /* + // Construct a random seed based on the initial guid value, and the last digits of the difference to each subsequent number + // This assumes that only the last 3 bits of each guidId integer has a meaningful amount of randomness between it and the prior number + unsigned int t = guid.g[0]; + unsigned int i; + for (i=1; i < sizeof(guid.g) / sizeof(guid.g[0]); i++) + { + unsigned int diff = guid.g[i]-guid.g[i-1]; + unsigned int diff3Bits = diff & 0x0007; + diff3Bits <<= 29; + diff3Bits >>= (i-1)*3; + t ^= diff3Bits; + } + + return t; + */ + return (unsigned int) ((myGuid.g >> 32) ^ myGuid.g); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +void RakPeer::DerefAllSockets(void) +{ + unsigned int i; + for (i=0; i < socketList.Size(); i++) + { + if (socketFactory && !socketList[i]->IsBerkleySocket()) + socketFactory->Destroy(socketList[i]); + else + MafiaNet::OP_DELETE(socketList[i], _FILE_AND_LINE_); + } + socketList.Clear(false, _FILE_AND_LINE_); +} +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +unsigned int RakPeer::GetRakNetSocketFromUserConnectionSocketIndex(unsigned int userIndex) const +{ + unsigned int i; + for (i=0; i < socketList.Size(); i++) + { + if (socketList[i]->GetUserConnectionSocketIndex()==userIndex) + return i; + } + RakAssert("GetRakNetSocketFromUserConnectionSocketIndex failed" && 0); + return (unsigned int) -1; +} + +/* +// DS_APR +void RakPeer::ProcessChromePacket(RakNetSocket2 *s, const char *buffer, int dataSize, const SystemAddress& recvFromAddress, MafiaNet::TimeUS timeRead) +{ + RakAssert(buffer); + RakAssert(dataSize > 0); + RakAssert(recvFromAddress.GetPort()); + + RNS2RecvStruct *recvFromStruct; + recvFromStruct=bufferedPackets.Allocate( _FILE_AND_LINE_ ); + RakAssert(dataSize <= (int)sizeof(recvFromStruct->data)); + memcpy(recvFromStruct->data, buffer, dataSize); + recvFromStruct->bytesRead=dataSize; + recvFromStruct->systemAddress=recvFromAddress; + recvFromStruct->timeRead=timeRead; + bufferedPackets.Push(recvFromStruct); +} +*/ + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +/* +bool RakPeer::RunRecvFromOnce( RakNetSocket *s ) +{ + RakPeer::RecvFromStruct *recvFromStruct; + + recvFromStruct=bufferedPackets.Allocate( _FILE_AND_LINE_ ); + if (recvFromStruct != nullptr) + { + recvFromStruct->s=s; + SocketLayer::RecvFromBlocking(s, this, recvFromStruct->data, &recvFromStruct->bytesRead, &recvFromStruct->systemAddress, &recvFromStruct->timeRead); + + if (recvFromStruct->bytesRead>0) + { + RakAssert(recvFromStruct->systemAddress.GetPort()); + bufferedPackets.Push(recvFromStruct); + quitAndDataEvents.SetEvent(); + + // Got data + return true; + } + else + { + bufferedPackets.Deallocate(recvFromStruct, _FILE_AND_LINE_); + } + } + // No data + return false; +} +*/ +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +bool RakPeer::RunUpdateCycle(BitStream &updateBitStream ) +{ + RakPeer::RemoteSystemStruct * remoteSystem; + unsigned int activeSystemListIndex; + Packet *packet; + // int currentSentBytes,currentReceivedBytes; +// unsigned numberOfBytesUsed; +// BitSize_t numberOfBitsUsed; + //SystemAddress authoritativeClientSystemAddress; + BitSize_t bitSize; + unsigned int byteSize; + unsigned char *data; + SystemAddress systemAddress; + BufferedCommandStruct *bcs; + bool callerDataAllocationUsed; + RakNetStatistics *rnss; + MafiaNet::TimeUS timeNS=0; + MafiaNet::Time timeMS=0; + + // This is here so RecvFromBlocking actually gets data from the same thread + +#if defined(_WIN32) + // #high - review this - this is hardly correct to only work with the first socket here... + if (socketList[0]->GetSocketType()==RNS2T_WINDOWS && ((RNS2_Windows*)socketList[0])->GetSocketLayerOverride()) + { + int len; + SystemAddress sender; + char dataOut[ MAXIMUM_MTU_SIZE ]; + do { + len = ((RNS2_Windows*)socketList[0])->GetSocketLayerOverride()->RakNetRecvFrom(dataOut,&sender,true); + if (len>0) + ProcessNetworkPacket( sender, dataOut, len, this, socketList[0], MafiaNet::GetTimeUS(), updateBitStream ); + } while (len>0); + } +#endif + +// unsigned int socketListIndex; + RNS2RecvStruct *recvFromStruct; + while ((recvFromStruct=PopBufferedPacket())!=0) + { + /* + for (socketListIndex=0; socketListIndex < socketList.Size(); socketListIndex++) + { + if ((RakNetSocket*) socketList[socketListIndex]==recvFromStruct->s) + break; + } + if (socketListIndex!=socketList.Size()) + */ + ProcessNetworkPacket(recvFromStruct->systemAddress, recvFromStruct->data, recvFromStruct->bytesRead, this, recvFromStruct->socket, recvFromStruct->timeRead, updateBitStream); + DeallocRNS2RecvStruct(recvFromStruct, _FILE_AND_LINE_); + } + + while ((bcs=bufferedCommands.PopInaccurate())!=0) + { + if (bcs->command==BufferedCommandStruct::BCS_SEND) + { + // GetTime is a very slow call so do it once and as late as possible + if (timeNS==0) + { + timeNS = MafiaNet::GetTimeUS(); + timeMS = (MafiaNet::TimeMS)(timeNS/(MafiaNet::TimeUS)1000); + } + + callerDataAllocationUsed=SendImmediate((char*)bcs->data, bcs->numberOfBitsToSend, bcs->priority, bcs->reliability, bcs->orderingChannel, bcs->systemIdentifier, bcs->broadcast, true, timeNS, bcs->receipt); + if ( callerDataAllocationUsed==false ) + rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); + + // Set the new connection state AFTER we call sendImmediate in case we are setting it to a disconnection state, which does not allow further sends + if (bcs->connectionMode!=RemoteSystemStruct::NO_ACTION ) + { + remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); + if (remoteSystem) + remoteSystem->connectMode=bcs->connectionMode; + } + } + else if (bcs->command==BufferedCommandStruct::BCS_SESSION_ACCEPT || bcs->command==BufferedCommandStruct::BCS_SESSION_REJECT) + { + // Validate here rather than in the user-thread API: only the network thread may read connectMode + // and the awaiting flag without racing the receive path. + RemoteSystemStruct *sessionSystem = GetRemoteSystem( bcs->systemIdentifier, true, true ); + // connectMode is checked as well as the awaiting flag: a peer that disconnects while the decision + // is queued leaves the flag set (the teardown path does not clear session state), and answering + // then would send on a dying connection and report a connection for a peer that has already gone. + if (sessionSystem && sessionSystem->isActive && + sessionSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && + sessionSystem->sessionConfigAwaitingLocalDecision) + { + sessionSystem->sessionConfigAwaitingLocalDecision = false; + const unsigned int sessionPayloadLength = (unsigned int) BITS_TO_BYTES(bcs->numberOfBitsToSend); + if (bcs->command==BufferedCommandStruct::BCS_SESSION_ACCEPT) + { + // With a pending pool the handshake may have started while the server was full, so the + // incoming limit is enforced here. The application should check first; this is the backstop + // that keeps two accepts racing one free slot from both getting it. + SessionAdmission::Limits limits; + limits.maxIncoming = GetMaximumIncomingConnections(); + limits.maxPending = maximumPendingSessions; + limits.maxPendingPerAddress = maximumPendingSessionsPerAddress; + SessionAdmission::Counts counts; + CountRemoteInitiatedPeers(UNASSIGNED_SYSTEM_ADDRESS, counts.connected, counts.exchanging, counts.pending, counts.pendingFromAddress); + if (SessionAdmission::MayAccept(limits, counts)) + SendSessionConfigResponse(sessionSystem, bcs->data, sessionPayloadLength); + else + SendSessionConfigRejection(sessionSystem, 0, 0); + } + else + SendSessionConfigRejection(sessionSystem, bcs->data, sessionPayloadLength); + } + if (bcs->data) + rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); + } + else if (bcs->command==BufferedCommandStruct::BCS_SESSION_STATUS) + { + // Only a server peer still awaiting its application's decision may be told anything, and telling + // it restarts the session timer here as well as on the client when it arrives. + RemoteSystemStruct *sessionSystem = GetRemoteSystem( bcs->systemIdentifier, true, true ); + if (sessionSystem && sessionSystem->isActive && + sessionSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && + sessionSystem->sessionConfigAwaitingLocalDecision && + sessionSystem->sessionConfigIsConnectingSide==false) + { + MafiaNet::BitStream statusStream; + statusStream.Write((MessageID)ID_SESSION_CONFIG_STATUS); + const unsigned int statusLength = (unsigned int) BITS_TO_BYTES(bcs->numberOfBitsToSend); + if (bcs->data && statusLength > 0) + statusStream.Write(bcs->data, statusLength); + SendImmediate((char*)statusStream.GetData(), statusStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, sessionSystem->systemAddress, false, false, MafiaNet::GetTimeUS(), 0); + sessionSystem->sessionConfigStartTime = MafiaNet::GetTimeMS(); + } + if (bcs->data) + rakFree_Ex(bcs->data, _FILE_AND_LINE_ ); + } + else if (bcs->command==BufferedCommandStruct::BCS_CLOSE_CONNECTION) + { + // bcs->socket may be null: the socket can be released between queuing this + // command and processing it here (e.g. during rapid connect/disconnect churn). + // RakAssert is a no-op in release (NDEBUG), so guard explicitly and fall back + // to the primary socket to avoid dereferencing null in CloseConnectionInternal2. + RakNetSocket2 *closeSocket = bcs->socket; + if (closeSocket == nullptr && socketList.Size() > 0) + closeSocket = socketList[0]; + if (closeSocket != nullptr) + CloseConnectionInternal2(bcs->systemIdentifier, false, true, bcs->orderingChannel, bcs->priority, *closeSocket); + } + else if (bcs->command==BufferedCommandStruct::BCS_CHANGE_SYSTEM_ADDRESS) + { + // Reroute + RakPeer::RemoteSystemStruct *rssFromGuid = GetRemoteSystem(bcs->systemIdentifier.rakNetGuid,true,true); + if (rssFromGuid!=0) + { + unsigned int existingSystemIndex = GetRemoteSystemIndex(rssFromGuid->systemAddress); + ReferenceRemoteSystem(bcs->systemIdentifier.systemAddress, existingSystemIndex); + } + } + else if (bcs->command==BufferedCommandStruct::BCS_GET_SOCKET) + { + SocketQueryOutput *sqo; + if (bcs->systemIdentifier.IsUndefined()) + { + sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); + sqo->sockets=socketList; + socketQueryOutput.Push(sqo); + } + else + { + remoteSystem=GetRemoteSystem( bcs->systemIdentifier, true, true ); + sqo = socketQueryOutput.Allocate( _FILE_AND_LINE_ ); + + sqo->sockets.Clear(false, _FILE_AND_LINE_); + if (remoteSystem) + { + sqo->sockets.Push(remoteSystem->rakNetSocket, _FILE_AND_LINE_ ); + } + else + { + // Leave empty smart pointer + } + socketQueryOutput.Push(sqo); + } + + } + +#ifdef _DEBUG + bcs->data=0; +#endif + + bufferedCommands.Deallocate(bcs, _FILE_AND_LINE_); + } + + if (requestedConnectionQueue.IsEmpty()==false) + { + if (timeNS==0) + { + timeNS = MafiaNet::GetTimeUS(); + timeMS = (MafiaNet::TimeMS)(timeNS/(MafiaNet::TimeUS)1000); + } + + bool condition1, condition2; + unsigned requestedConnectionQueueIndex=0; + requestedConnectionQueueMutex.Lock(); + while (requestedConnectionQueueIndex < requestedConnectionQueue.Size()) + { + RequestedConnectionStruct *rcs; + rcs = requestedConnectionQueue[requestedConnectionQueueIndex]; + requestedConnectionQueueMutex.Unlock(); + if (rcs->nextRequestTime < timeMS) + { + condition1=rcs->requestsMade==rcs->sendConnectionAttemptCount+1; + condition2=(bool)((rcs->systemAddress==UNASSIGNED_SYSTEM_ADDRESS)==1); + // If too many requests made or a hole then remove this if possible, otherwise invalidate it + if (condition1 || condition2) + { + if (rcs->data) + { + rakFree_Ex(rcs->data, _FILE_AND_LINE_ ); + rcs->data=0; + } + + if (condition1 && !condition2 && rcs->actionToTake==RequestedConnectionStruct::CONNECT) + { + // Tell user of connection attempt failed + packet=AllocPacket(sizeof( char ), _FILE_AND_LINE_); + packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't + packet->bitSize = ( sizeof( char ) * 8); + packet->systemAddress = rcs->systemAddress; + AddPacketToProducer(packet); + } + +#if LIBCAT_SECURITY==1 + CAT_AUDIT_PRINTF("AUDIT: Connection attempt FAILED so deleting rcs->client_handshake object %x\n", rcs->client_handshake); + MafiaNet::OP_DELETE(rcs->client_handshake,_FILE_AND_LINE_); +#endif + MafiaNet::OP_DELETE(rcs,_FILE_AND_LINE_); + + requestedConnectionQueueMutex.Lock(); + for (unsigned int k=0; k < requestedConnectionQueue.Size(); k++) + { + if (requestedConnectionQueue[k]==rcs) + { + requestedConnectionQueue.RemoveAtIndex(k); + break; + } + } + requestedConnectionQueueMutex.Unlock(); + } + else + { + + const unsigned attemptsPerMTU = GetConnectionAttemptsPerMTU(rcs->sendConnectionAttemptCount); + int MTUSizeIndex = rcs->requestsMade / attemptsPerMTU; + if (MTUSizeIndex>=NUM_MTU_SIZES) + MTUSizeIndex=NUM_MTU_SIZES-1; + rcs->requestsMade++; + rcs->nextRequestTime=timeMS+rcs->timeBetweenSendConnectionAttemptsMS; + + MafiaNet::BitStream bitStream; + //WriteOutOfBandHeader(&bitStream, ID_USER_PACKET_ENUM); + bitStream.Write((MessageID)ID_OPEN_CONNECTION_REQUEST_1); + bitStream.WriteAlignedBytes((const unsigned char*) OFFLINE_MESSAGE_DATA_ID, sizeof(OFFLINE_MESSAGE_DATA_ID)); + bitStream.Write((MessageID)RAKNET_PROTOCOL_VERSION); + bitStream.PadWithZeroToByteLength(mtuSizes[MTUSizeIndex]-UDP_HEADER_SIZE); + + char str[256]; + rcs->systemAddress.ToString(true,str,static_cast(256)); + + //RAKNET_DEBUG_PRINTF("%i:IOCR, ", __LINE__); + + unsigned i; + for (i=0; i < pluginListNTS.Size(); i++) + pluginListNTS[i]->OnDirectSocketSend((const char*) bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), rcs->systemAddress); + + RakNetSocket2 *socketToUse; + if (rcs->socket == 0) + socketToUse = socketList[rcs->socketIndex]; + else + socketToUse = rcs->socket; + + rcs->systemAddress.FixForIPVersion(socketToUse->GetBoundAddress()); + if (socketToUse->IsBerkleySocket()) + ((RNS2_Berkley*)socketToUse)->SetDoNotFragment(1); + +// SocketLayer::SetDoNotFragment(socketToUse, 1); + MafiaNet::Time sendToStart= MafiaNet::GetTime(); + + RNS2_SendParameters bsp; + bsp.data = (char*) bitStream.GetData(); + bsp.length = bitStream.GetNumberOfBytesUsed(); + bsp.systemAddress = rcs->systemAddress; + const RNS2SendResult sendResult = socketToUse->Send(&bsp, _FILE_AND_LINE_); + // Send() reports failure as sendto's own return value and drops the reason, so + // the error has to be read back off the thread before anything else touches a + // socket. The old test here compared that return value against 10040 directly, + // which sendto never yields -- it yields SOCKET_ERROR -- so this branch was dead + // and a locally-refused MTU burned its rung's entire attempt budget. + if (sendResult < 0 && RNS2_IsDatagramTooLargeError(RNS2_GetLastSocketError())) + { + // The local interface refused the datagram outright. Every attempt at this + // size fails the same way, so step down now. + rcs->requestsMade = (unsigned char) ((MTUSizeIndex + 1) * attemptsPerMTU); + rcs->nextRequestTime=timeMS; + } + else + { + MafiaNet::Time sendToEnd= MafiaNet::GetTime(); + if (sendToEnd-sendToStart>100) + { + // A send that blocks this long means the outgoing interface is + // struggling: a virtual VPN adapter renegotiating its tunnel, or a full + // transmit queue. Smaller datagrams are likelier to get through, so drop + // to the lowest MTU -- but keep trying. Giving up here (which is what + // this did when already on the lowest rung) turned a transient stall + // into ID_CONNECTION_ATTEMPT_FAILED with most of the budget unspent. + const int lowestMtuIndex = (int) (attemptsPerMTU * (unsigned) (NUM_MTU_SIZES - 1)); + if (lowestMtuIndex > (int) rcs->requestsMade) + { + rcs->requestsMade = (unsigned char) lowestMtuIndex; + rcs->nextRequestTime=timeMS; + } + } + } + // SocketLayer::SetDoNotFragment(socketToUse, 0); + if (socketToUse->IsBerkleySocket()) + ((RNS2_Berkley*)socketToUse)->SetDoNotFragment(0); + + requestedConnectionQueueIndex++; + } + } + else + requestedConnectionQueueIndex++; + + requestedConnectionQueueMutex.Lock(); + } + requestedConnectionQueueMutex.Unlock(); + } + + // remoteSystemList in network thread + for ( activeSystemListIndex = 0; activeSystemListIndex < activeSystemListSize; ++activeSystemListIndex ) + //for ( remoteSystemIndex = 0; remoteSystemIndex < remoteSystemListSize; ++remoteSystemIndex ) + { + // I'm using systemAddress from remoteSystemList but am not locking it because this loop is called very frequently and it doesn't + // matter if we miss or do an extra update. The reliability layers themselves never care which player they are associated with + //systemAddress = remoteSystemList[ remoteSystemIndex ].systemAddress; + // Allow the systemAddress for this remote system list to change. We don't care if it changes now. + // remoteSystemList[ remoteSystemIndex ].allowSystemAddressAssigment=true; + + + // Found an active remote system + remoteSystem = activeSystemList[ activeSystemListIndex ]; + systemAddress = remoteSystem->systemAddress; + RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS); + // Update is only safe to call from the same thread that calls HandleSocketReceiveFromConnectedPlayer, + // which is this thread + + if (timeNS==0) + { + timeNS = MafiaNet::GetTimeUS(); + timeMS = (MafiaNet::TimeMS)(timeNS/(MafiaNet::TimeUS)1000); + //RAKNET_DEBUG_PRINTF("timeNS = %I64i timeMS=%i\n", timeNS, timeMS); + } + + + if (timeMS > remoteSystem->lastReliableSend && timeMS-remoteSystem->lastReliableSend > remoteSystem->reliabilityLayer.GetTimeoutTime()/2 && remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) + { + // If no reliable packets are waiting for an ack, do a one byte reliable send so that disconnections are noticed + RakNetStatistics rakNetStatistics; + rnss=remoteSystem->reliabilityLayer.GetStatistics(&rakNetStatistics); + if (rnss->messagesInResendBuffer==0) + { + PingInternal( systemAddress, true, MafiaNet::Reliability::Reliable ); + + //remoteSystem->lastReliableSend=timeMS+remoteSystem->reliabilityLayer.GetTimeoutTime(); + remoteSystem->lastReliableSend=timeMS; + } + } + + if (endThreads) + // for the final call, make sure we send out any outstanding ACKs + remoteSystem->reliabilityLayer.UpdateAndForceACKs( remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream ); // systemAddress only used for the internet simulator test + else + remoteSystem->reliabilityLayer.Update( remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream ); // systemAddress only used for the internet simulator test + + // The reliability layer steps the negotiated MTU down when it + // detects an in-session path-MTU black hole; keep the value + // GetMTUSize() reports in sync with what is actually on the wire. + // Written only on an actual step-down: GetMTUSize() reads this + // field from the user thread without synchronization (as it always + // has for the connect-time write), so avoid turning it into a + // continuously-written field. + const int reliabilityMtu = remoteSystem->reliabilityLayer.GetCurrentMtuBytes(); + if (remoteSystem->MTUSize != reliabilityMtu) + remoteSystem->MTUSize = reliabilityMtu; + + // Check for failure conditions + if ( remoteSystem->reliabilityLayer.IsDeadConnection() || + ((remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY) && remoteSystem->reliabilityLayer.IsOutgoingDataWaiting()==false) || + (remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK && (remoteSystem->reliabilityLayer.AreAcksWaiting()==false || remoteSystem->reliabilityLayer.AckTimeout(timeMS)==true)) || + (( + (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || + remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || + remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) + && timeMS > remoteSystem->connectionTime && timeMS - remoteSystem->connectionTime > 10000)) + ) + { + // RAKNET_DEBUG_PRINTF("timeMS=%i remoteSystem->connectionTime=%i\n", timeMS, remoteSystem->connectionTime ); + + // Before the close notification logic: a held session request is not a reported connection, but + // the application is holding a decision for it and has to learn it will never be needed. + ProduceSessionAbandoned(remoteSystem); + + // Failed. Inform the user? + // TODO - RakNet 4.0 - Return a different message identifier for DISCONNECT_ASAP_SILENTLY and DISCONNECT_ASAP than for DISCONNECT_ON_NO_ACK + // The first two mean we called CloseConnection(), the last means the other system sent us ID_DISCONNECTION_NOTIFICATION + // A connection the application was never told about must not produce a close notification for + // it. On the accepting side that covers a peer refused by RejectSession() (which parks the slot + // in DISCONNECT_ON_NO_ACK so the refusal is delivered first) and one dropped mid-handshake. + // The connecting side is exempt: it has an outstanding Connect() to resolve either way. + const bool reportableConnection = remoteSystem->connectionReportedToApplication || remoteSystem->weInitiatedTheConnection; + if (reportableConnection && + (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED || remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION + || remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA + || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP || remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK)) + { + +// MafiaNet::BitStream undeliveredMessages; +// remoteSystem->reliabilityLayer.GetUndeliveredMessages(&undeliveredMessages,remoteSystem->MTUSize); + +// packet=AllocPacket(sizeof( char ) + undeliveredMessages.GetNumberOfBytesUsed()); + // A graceful remote disconnect (DISCONNECT_ON_NO_ACK) may carry a reason payload stashed when the + // notification arrived. Only ID_DISCONNECTION_NOTIFICATION carries it; ID_CONNECTION_LOST and + // ID_CONNECTION_ATTEMPT_FAILED are locally synthesized and stay payload-less. + const bool attachReason = remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ON_NO_ACK && remoteSystem->disconnectReasonData!=0; + const unsigned int reasonLength = attachReason ? remoteSystem->disconnectReasonLength : 0; + packet=AllocPacket(sizeof( char ) + reasonLength, _FILE_AND_LINE_); + if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) + packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't + else if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA) + // Dropped mid-session-handshake. The connecting side still has an attempt outstanding; the + // accepting side never reported anything, so a lost connection is all it can report. + packet->data[ 0 ] = remoteSystem->sessionConfigIsConnectingSide + ? (unsigned char)ID_CONNECTION_ATTEMPT_FAILED : (unsigned char)ID_CONNECTION_LOST; + else if (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) + packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection + else + packet->data[ 0 ] = ID_DISCONNECTION_NOTIFICATION; // DeadConnection + + if (attachReason) + memcpy(packet->data + sizeof(unsigned char), remoteSystem->disconnectReasonData, reasonLength); + +// memcpy(packet->data+1, undeliveredMessages.GetData(), undeliveredMessages.GetNumberOfBytesUsed()); + + packet->guid = remoteSystem->guid; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + + AddPacketToProducer(packet); + } + // else connection shutting down, don't bother telling the user + +#ifdef _DO_PRINTF + RAKNET_DEBUG_PRINTF("Connection dropped for player %i:%i\n", systemAddress); +#endif + // we are about to close the connection to the remote system, we'd still make sure to send any outstanding ACKs, so for the remote system not unnecessarily waiting for these until its timeout + // (and trying to resend them unnecessarily) + remoteSystem->reliabilityLayer.UpdateAndForceACKs(remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream); + + CloseConnectionInternal2(systemAddress, false, true, 0, MafiaNet::Priority::Low, *(remoteSystem->rakNetSocket)); + continue; + } + + // A session handshake that never completes must not pin the slot. Acks keep flowing in + // EXCHANGING_SESSION_DATA, so the ordinary dead-connection detection above would never fire; bound + // it by the same timeout instead. Interactive servers put an application in the middle of this + // exchange, so it is a reachable state, not a theoretical one. + if ( remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && + remoteSystem->sessionConfigStartTime!=0 && + timeMS > remoteSystem->sessionConfigStartTime && + timeMS - remoteSystem->sessionConfigStartTime > GetSessionTimeoutFor(remoteSystem) ) + { + // Only the connecting side reports a failure; a server just drops the half-open slot. + if (remoteSystem->sessionConfigIsConnectingSide) + { + Packet *timeoutPacket=AllocPacket(sizeof(MessageID), _FILE_AND_LINE_); + timeoutPacket->data[0]=(unsigned char)ID_CONNECTION_ATTEMPT_FAILED; + timeoutPacket->bitSize=sizeof(MessageID)*8; + timeoutPacket->systemAddress=remoteSystem->systemAddress; + timeoutPacket->systemAddress.systemIndex=remoteSystem->remoteSystemIndex; + timeoutPacket->guid=remoteSystem->guid; + timeoutPacket->guid.systemIndex=timeoutPacket->systemAddress.systemIndex; + AddPacketToProducer(timeoutPacket); + } + else + { + ProduceSessionAbandoned(remoteSystem); + } + + remoteSystem->reliabilityLayer.UpdateAndForceACKs(remoteSystem->rakNetSocket, systemAddress, remoteSystem->MTUSize, timeNS, maxOutgoingBPS, pluginListNTS, &rnr, updateBitStream); + CloseConnectionInternal2(systemAddress, false, true, 0, MafiaNet::Priority::Low, *(remoteSystem->rakNetSocket)); + continue; + } + + // Ping this guy if it is time to do so + if ( remoteSystem->connectMode==RemoteSystemStruct::CONNECTED && timeMS > remoteSystem->nextPingTime && ( occasionalPing || remoteSystem->lowestPing == (unsigned short)-1 ) ) + { + remoteSystem->nextPingTime = timeMS + 5000; + PingInternal( systemAddress, true, MafiaNet::Reliability::Unreliable ); + + // Update again immediately after this tick so the ping goes out right away + quitAndDataEvents.SetEvent(); + } + + // Find whoever has the lowest player ID + //if (systemAddress < authoritativeClientSystemAddress) + // authoritativeClientSystemAddress=systemAddress; + + // Does the reliability layer have any packets waiting for us? + // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer + bitSize = remoteSystem->reliabilityLayer.Receive( &data ); + + while ( bitSize > 0 ) + { + // These types are for internal use and should never arrive from a network packet + if (data[0]==ID_CONNECTION_ATTEMPT_FAILED) + { + RakAssert(0); + bitSize=0; + continue; + } + + // Fast and easy - just use the data that was returned + byteSize = (unsigned int) BITS_TO_BYTES( bitSize ); + + // For unknown senders we only accept a few specific packets + if (remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) + { + if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) + { + ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else + { + CloseConnectionInternal2(systemAddress, false, true, 0, MafiaNet::Priority::Low, *(remoteSystem->rakNetSocket)); +#ifdef _DO_PRINTF + RAKNET_DEBUG_PRINTF("Temporarily banning %i:%i for sending nonsense data\n", systemAddress); +#endif + + char str1[64]; + systemAddress.ToString(false, str1, static_cast(64)); + AddToBanList(str1, remoteSystem->reliabilityLayer.GetTimeoutTime()); + + + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else + { + // However, if we are connected we still take a connection request in case both systems are trying to connect to each other + // at the same time + if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) + { + // 04/27/06 This is wrong. With cross connections, we can both have initiated the connection are in state REQUESTED_CONNECTION + // 04/28/06 Downgrading connections from connected will close the connection due to security at ((remoteSystem->connectMode!=RemoteSystemStruct::CONNECTED && time > remoteSystem->connectionTime && time - remoteSystem->connectionTime > 10000)) + if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) + { + ParseConnectionRequestPacket(remoteSystem, systemAddress, (const char*)data, byteSize); + } + else + { + + MafiaNet::BitStream bs((unsigned char*) data,byteSize,false); + bs.IgnoreBytes(sizeof(MessageID)); + bs.IgnoreBytes(sizeof(OFFLINE_MESSAGE_DATA_ID)); + bs.IgnoreBytes(RakNetGUID::size()); + MafiaNet::Time incomingTimestamp; + bs.Read(incomingTimestamp); + + // Got a connection request message from someone we are already connected to. Just reply normally. + // This can happen due to race conditions with the fully connected mesh + OnConnectionRequest( remoteSystem, incomingTimestamp ); + } + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char) data[ 0 ] == ID_NEW_INCOMING_CONNECTION && byteSize > sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(MafiaNet::Time)*2 ) + { + if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) + { + // Transport is up, but the application is not told so until the client's session payload + // arrives in ID_SESSION_CONFIG_REQUEST and we answer it. + remoteSystem->connectMode=RemoteSystemStruct::EXCHANGING_SESSION_DATA; + remoteSystem->sessionConfigIsConnectingSide=false; + remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); + PingInternal( systemAddress, true, MafiaNet::Reliability::Unreliable ); + + // Update again immediately after this tick so the ping goes out right away + quitAndDataEvents.SetEvent(); + + MafiaNet::BitStream inBitStream((unsigned char *) data, byteSize, false); + SystemAddress bsSystemAddress; + + inBitStream.IgnoreBits(8); + inBitStream.Read(bsSystemAddress); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); + + MafiaNet::Time sendPingTime, sendPongTime; + inBitStream.Read(sendPingTime); + inBitStream.Read(sendPongTime); + OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); + + // Overwrite the data in the packet + // NewIncomingConnectionStruct newIncomingConnectionStruct; + // MafiaNet::BitStream nICS_BS( data, NewIncomingConnectionStruct_Size, false ); + // newIncomingConnectionStruct.Deserialize( nICS_BS ); + + remoteSystem->myExternalSystemAddress = bsSystemAddress; + + // Bug: If A connects to B through R, A's firstExternalID is set to R. If A tries to send to R, sends to loopback because R==firstExternalID + // Correct fix is to specify in Connect() if target is through a proxy. + // However, in practice you have to connect to something else first anyway to know about the proxy. So setting once only is good enough + if (firstExternalID==UNASSIGNED_SYSTEM_ADDRESS) + { + firstExternalID=bsSystemAddress; + firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); + } + + // Hold this until the session handshake completes; the bytes are copied because `data` + // is freed as soon as this branch returns. + ClearSessionConfig(remoteSystem); + remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); + remoteSystem->withheldConnectionPacketData = (unsigned char*) rakMalloc_Ex(byteSize, _FILE_AND_LINE_); + if (remoteSystem->withheldConnectionPacketData!=0) + { + memcpy(remoteSystem->withheldConnectionPacketData, data, byteSize); + remoteSystem->withheldConnectionPacketLength = byteSize; + } + // AllocPacket used to take ownership of `data` here; the copy above means this branch does. + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else + { + // Send to game even if already connected. This could happen when connecting to 127.0.0.1 + // Ignore, already connected + // rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else if ( (unsigned char) data[ 0 ] == ID_CONNECTED_PONG && byteSize == sizeof(unsigned char)+sizeof(MafiaNet::Time)*2 ) + { + MafiaNet::Time sendPingTime, sendPongTime; + + // Copy into the ping times array the current time - the value returned + // First extract the sent ping + MafiaNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); + //PingStruct ps; + //ps.Deserialize(psBS); + inBitStream.IgnoreBits(8); + inBitStream.Read(sendPingTime); + inBitStream.Read(sendPongTime); + + OnConnectedPong(sendPingTime,sendPongTime,remoteSystem); + + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char)data[0] == ID_CONNECTED_PING && byteSize == sizeof(unsigned char)+sizeof(MafiaNet::Time) ) + { + MafiaNet::BitStream inBitStream( (unsigned char *) data, byteSize, false ); + inBitStream.IgnoreBits(8); + MafiaNet::Time sendPingTime; + inBitStream.Read(sendPingTime); + + MafiaNet::BitStream outBitStream; + outBitStream.Write((MessageID)ID_CONNECTED_PONG); + outBitStream.Write(sendPingTime); + outBitStream.Write(MafiaNet::GetTime()); + SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::Unreliable, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0 ); + + // Update again immediately after this tick so the ping goes out right away + quitAndDataEvents.SetEvent(); + + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char) data[ 0 ] == ID_DISCONNECTION_NOTIFICATION ) + { + // Stash any reason payload (everything after the 1-byte ID) so it can ride along with the + // user-facing notification packet synthesized once outstanding ACKs are flushed. This raw + // reliability-layer buffer is freed below, so the bytes have to be copied out now. + ClearDisconnectReason(remoteSystem); + if (byteSize > sizeof(unsigned char)) + { + const unsigned int reasonLength = byteSize - (unsigned int) sizeof(unsigned char); + remoteSystem->disconnectReasonData = (unsigned char*) rakMalloc_Ex(reasonLength, _FILE_AND_LINE_); + if (remoteSystem->disconnectReasonData != 0) + { + memcpy(remoteSystem->disconnectReasonData, data + sizeof(unsigned char), reasonLength); + remoteSystem->disconnectReasonLength = reasonLength; + } + } + + // We shouldn't close the connection immediately because we need to ack the ID_DISCONNECTION_NOTIFICATION + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ON_NO_ACK; + rakFree_Ex(data, _FILE_AND_LINE_ ); + + // AddPacketToProducer(packet); + } + else if ( (unsigned char)(data)[0] == ID_DETECT_LOST_CONNECTIONS && byteSize == sizeof(unsigned char) ) + { + // Do nothing + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG_REQUEST ) + { + // Server side of the session handshake. The client's payload arrives before we answer, so a + // server can inspect it and refuse without ever having disclosed its own. + if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigAwaitingLocalDecision==false) + { + if (StoreRemoteSessionConfig(remoteSystem, (const unsigned char*)data + sizeof(MessageID), byteSize - (unsigned int)sizeof(MessageID))==false) + { + // Oversized payload is a protocol violation; drop without answering. + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if (sessionConfigInteractive) + { + // Let the application decide. The connection stays unreported until it calls + // AcceptSession() or RejectSession(), or the handshake times out below. + remoteSystem->sessionConfigAwaitingLocalDecision=true; + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + } + else + { + rakPeerMutexes[ sessionConfig_Mutex ].Lock(); + SendSessionConfigResponse(remoteSystem, (const char*)sessionConfig.GetData(), (unsigned int)sessionConfig.GetNumberOfBytesUsed()); + rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else + { + // A repeat request, or one on an already-established connection: ignore it rather than + // letting a peer re-run the handshake and re-produce the connection packet. + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG ) + { + // Client side of the session handshake: the server's payload is the last thing we needed, so + // the withheld ID_CONNECTION_REQUEST_ACCEPTED goes out now and the connection becomes real. + // Only the peer that initiated the connection may be answered this way, so a client cannot + // push a payload at a server by replying with the server's own message. + if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigIsConnectingSide) + { + if (StoreRemoteSessionConfig(remoteSystem, (const unsigned char*)data + sizeof(MessageID), byteSize - (unsigned int)sizeof(MessageID))==false) + { + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + } + else + { + remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; + remoteSystem->sessionConfigStartTime=0; + ProduceWithheldConnectionPacket(remoteSystem, (MessageID)ID_CONNECTION_REQUEST_ACCEPTED); + } + } + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG_STATUS ) + { + // Client side, while the server's application decides: surface the status and restart our own + // session timer, which is what lets a queued client wait longer than the timeout. Bound to the + // connecting side like the other replies, so a client cannot push one at a server. + if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigIsConnectingSide) + { + remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + } + else + { + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else if ( (unsigned char)(data)[0] == ID_SESSION_CONFIG_REJECTED ) + { + // Only meaningful to the peer that initiated the connection. Without this a malicious client + // could inject ID_CONNECTION_ATTEMPT_FAILED -- a message the application only ever expects for + // its own outbound connects -- into a listening server's queue. + if (remoteSystem->connectMode==RemoteSystemStruct::EXCHANGING_SESSION_DATA && remoteSystem->sessionConfigIsConnectingSide) + { + // Rewrite in place so the reason string the server supplied stays at data+1, matching the + // ID_DISCONNECTION_NOTIFICATION reason convention. No connection is ever reported. + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->data[0]=(unsigned char)ID_CONNECTION_ATTEMPT_FAILED; + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + } + else + { + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else if ( (unsigned char)(data)[0] == ID_INVALID_PASSWORD ) + { + if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) + { + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + AddPacketToProducer(packet); + + remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP_SILENTLY; + } + else + { + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST_ACCEPTED ) + { + if (byteSize > sizeof(MessageID)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(SystemIndex)+sizeof(MafiaNet::Time)*2) + { + // Make sure this connection accept is from someone we wanted to connect to + bool allowConnection, alreadyConnected; + + if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || + remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || + allowConnectionResponseIPMigration) + allowConnection=true; + else + allowConnection=false; + + if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) + alreadyConnected=true; + else + alreadyConnected=false; + + if ( allowConnection ) + { + SystemAddress externalID; + SystemIndex systemIndex; +// SystemAddress internalID; + + MafiaNet::BitStream inBitStream((unsigned char *) data, byteSize, false); + inBitStream.IgnoreBits(8); + // inBitStream.Read(remotePort); + inBitStream.Read(externalID); + inBitStream.Read(systemIndex); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + inBitStream.Read(remoteSystem->theirInternalSystemAddress[i]); + + MafiaNet::Time sendPingTime, sendPongTime; + inBitStream.Read(sendPingTime); + inBitStream.Read(sendPongTime); + OnConnectedPong(sendPingTime, sendPongTime, remoteSystem); + + // Find a free remote system struct to use + // MafiaNet::BitStream casBitS(data, byteSize, false); + // ConnectionAcceptStruct cas; + // cas.Deserialize(casBitS); + // systemAddress.GetPort() = remotePort; + + // The remote system told us our external IP, so save it + remoteSystem->myExternalSystemAddress = externalID; + // Transport is up, but the application is not told so until the session payloads have been + // exchanged. ID_CONNECTION_REQUEST_ACCEPTED is stashed and replayed from ID_SESSION_CONFIG. + remoteSystem->connectMode=RemoteSystemStruct::EXCHANGING_SESSION_DATA; + remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); + + // Bug: If A connects to B through R, A's firstExternalID is set to R. If A tries to send to R, sends to loopback because R==firstExternalID + // Correct fix is to specify in Connect() if target is through a proxy. + // However, in practice you have to connect to something else first anyway to know about the proxy. So setting once only is good enough + if (firstExternalID==UNASSIGNED_SYSTEM_ADDRESS) + { + firstExternalID=externalID; + firstExternalID.debugPort=ntohs(firstExternalID.address.addr4.sin_port); + } + + // Hold the connection-complete packet until the session handshake finishes. The bytes are + // copied because `data` is freed as soon as this branch returns. + ClearSessionConfig(remoteSystem); + remoteSystem->sessionConfigIsConnectingSide=true; + remoteSystem->sessionConfigStartTime=timeMS!=0 ? timeMS : MafiaNet::GetTimeMS(); + remoteSystem->withheldConnectionPacketData = (unsigned char*) rakMalloc_Ex(byteSize, _FILE_AND_LINE_); + if (remoteSystem->withheldConnectionPacketData!=0) + { + memcpy(remoteSystem->withheldConnectionPacketData, data, byteSize); + remoteSystem->withheldConnectionPacketLength = byteSize; + } + // AllocPacket used to take ownership of `data` here; the copy above means this branch does. + rakFree_Ex(data, _FILE_AND_LINE_ ); + + MafiaNet::BitStream outBitStream; + outBitStream.Write((MessageID)ID_NEW_INCOMING_CONNECTION); + outBitStream.Write(systemAddress); + for (unsigned int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++) + outBitStream.Write(ipList[i]); + outBitStream.Write(sendPongTime); + outBitStream.Write(MafiaNet::GetTime()); + + SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0 ); + + // Our half of the session handshake. Sent on the same ordered channel right behind + // ID_NEW_INCOMING_CONNECTION, so the server has the transport connection before this lands. + { + MafiaNet::BitStream sessionBitStream; + sessionBitStream.Write((MessageID)ID_SESSION_CONFIG_REQUEST); + rakPeerMutexes[ sessionConfig_Mutex ].Lock(); + if (sessionConfig.GetNumberOfBytesUsed() > 0) + sessionBitStream.Write((const char*)sessionConfig.GetData(), sessionConfig.GetNumberOfBytesUsed()); + rakPeerMutexes[ sessionConfig_Mutex ].Unlock(); + SendImmediate( (char*)sessionBitStream.GetData(), sessionBitStream.GetNumberOfBitsUsed(), MafiaNet::Priority::Immediate, MafiaNet::Reliability::ReliableOrdered, 0, systemAddress, false, false, MafiaNet::GetTimeUS(), 0 ); + } + + if (alreadyConnected==false) + { + PingInternal( systemAddress, true, MafiaNet::Reliability::Unreliable ); + } + } + else + { + // Ignore, already connected + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else + { + // Version mismatch error? + RakAssert(0); + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + else + { + // What do I do if I get a message from a system, before I am fully connected? + // I can either ignore it or give it to the user + // It seems like giving it to the user is a better option + // + // Except while the session handshake is running: the application has not been told this + // connection exists, so handing it application data from that peer would deliver a packet + // for a guid it has never seen connect -- and from a peer it may still refuse. A peer that + // respects the protocol cannot send here anyway: its own public send path is gated on the + // same state, so anything arriving in this window is malformed or hostile. + if ((data[0]>=(MessageID)ID_TIMESTAMP || data[0]==ID_SND_RECEIPT_ACKED || data[0]==ID_SND_RECEIPT_LOSS) && + remoteSystem->isActive && + remoteSystem->connectMode!=RemoteSystemStruct::EXCHANGING_SESSION_DATA + ) + { + packet=AllocPacket(byteSize, data, _FILE_AND_LINE_); + packet->bitSize = bitSize; + packet->systemAddress = systemAddress; + packet->systemAddress.systemIndex = remoteSystem->remoteSystemIndex; + packet->guid = remoteSystem->guid; + packet->guid.systemIndex=packet->systemAddress.systemIndex; + if (!DispatchNetworkThreadReceive(packet)) + AddPacketToProducer(packet); + } + else + { + rakFree_Ex(data, _FILE_AND_LINE_ ); + } + } + } + + // Does the reliability layer have any more packets waiting for us? + // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer + bitSize = remoteSystem->reliabilityLayer.Receive( &data ); + } + + } + + return true; +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +void RakPeer::OnRNS2Recv(RNS2RecvStruct *recvStruct) +{ + if (incomingDatagramEventHandler) + { + if (incomingDatagramEventHandler(recvStruct)!=true) + return; + } + + PushBufferedPacket(recvStruct); + quitAndDataEvents.SetEvent(); +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +RAK_THREAD_DECLARATION(MafiaNet::UpdateNetworkLoop) +{ + + + + RakPeer * rakPeer = ( RakPeer * ) arguments; + + +/* + // 11/15/05 - this is slower than Sleep() +#ifdef _WIN32 +#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) + // Lets see if these timers give better performance than Sleep + HANDLE timerHandle; + LARGE_INTEGER dueTime; + + if ( rakPeer->threadSleepTimer <= 0 ) + rakPeer->threadSleepTimer = 1; + + // 2nd parameter of false means synchronization timer instead of manual-reset timer + timerHandle = CreateWaitableTimer( nullptr, FALSE, 0 ); + + RakAssert( timerHandle ); + + dueTime.QuadPart = -10000 * rakPeer->threadSleepTimer; // 10000 is 1 ms? + + BOOL success = SetWaitableTimer( timerHandle, &dueTime, rakPeer->threadSleepTimer, nullptr, nullptr, FALSE ); + (void) success; + RakAssert( success ); + +#endif +#endif +*/ + + BitStream updateBitStream( MAXIMUM_MTU_SIZE +#if LIBCAT_SECURITY==1 + + cat::AuthenticatedEncryption::OVERHEAD_BYTES +#endif + ); +// + rakPeer->isMainLoopThreadActive = true; + + bool running = true; + while ( running ) + { + if (rakPeer->endThreads) { + // allow just one more final update-run prior to shutting down this thread to make sure that outstanding acks are still sent and the peers don't unnecessary wait for already retrieved packets + // note: this fixes part of SLNET-123 + running = false; + } +// #ifdef _DEBUG +// // Sanity check, make sure RunUpdateCycle does not block or not otherwise get called for a long time +// RakNetTime thisCall=MafiaNet::GetTime(); +// RakAssert(thisCall-lastCall<250); +// lastCall=thisCall; +// #endif + if (rakPeer->userUpdateThreadPtr) + rakPeer->userUpdateThreadPtr(rakPeer, rakPeer->userUpdateThreadData); + + rakPeer->RunUpdateCycle(updateBitStream); + + // Pending sends go out this often, unless quitAndDataEvents is set + rakPeer->quitAndDataEvents.WaitOnEvent(10); + + /* + +// #if ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && +#if defined(USE_WAIT_FOR_MULTIPLE_EVENTS) && defined(_WIN32) + + if (rakPeer->threadSleepTimer>0) + { + WSAEVENT eventArray[256]; + unsigned int i, eventArrayIndex; + for (i=0,eventArrayIndex=0; i < rakPeer->socketList.Size(); i++) + { + if (rakPeer->socketList[i]->recvEvent!=INVALID_HANDLE_VALUE) + { + eventArray[eventArrayIndex]=rakPeer->socketList[i]->recvEvent; + eventArrayIndex++; + if (eventArrayIndex==256) + break; + } + } + WSAWaitForMultipleEvents(eventArrayIndex,(const HANDLE*) &eventArray,FALSE,rakPeer->threadSleepTimer,FALSE); + } + else + { + RakSleep(0); + } + +#else // ((_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) + #pragma message("-- RakNet: Using Sleep(). Uncomment USE_WAIT_FOR_MULTIPLE_EVENTS in defines.h if you want to use WaitForSingleObject instead. --") + + RakSleep( rakPeer->threadSleepTimer ); +#endif + */ + } + + rakPeer->isMainLoopThreadActive = false; + + /* +#ifdef _WIN32 +#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) + CloseHandle(timerHandle); +#endif +#endif + */ + + + + + return 0; + +} + +void RakPeer::CallPluginCallbacks(DataStructures::List &pluginList, Packet *packet) +{ + for (unsigned int i=0; i < pluginList.Size(); i++) + { + switch (packet->data[0]) + { + case ID_DISCONNECTION_NOTIFICATION: + pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_DISCONNECTION_NOTIFICATION); + break; + case ID_CONNECTION_LOST: + pluginList[i]->OnClosedConnection(packet->systemAddress, packet->guid, LCR_CONNECTION_LOST); + break; + case ID_NEW_INCOMING_CONNECTION: + pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, true); + break; + case ID_CONNECTION_REQUEST_ACCEPTED: + pluginList[i]->OnNewConnection(packet->systemAddress, packet->guid, false); + break; + case ID_CONNECTION_ATTEMPT_FAILED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_ATTEMPT_FAILED); + break; + case ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY); + break; + case ID_OUR_SYSTEM_REQUIRES_SECURITY: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_OUR_SYSTEM_REQUIRES_SECURITY); + break; + case ID_PUBLIC_KEY_MISMATCH: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_PUBLIC_KEY_MISMATCH); + break; + case ID_ALREADY_CONNECTED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_ALREADY_CONNECTED); + break; + case ID_NO_FREE_INCOMING_CONNECTIONS: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_NO_FREE_INCOMING_CONNECTIONS); + break; + case ID_CONNECTION_BANNED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_CONNECTION_BANNED); + break; + case ID_INVALID_PASSWORD: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INVALID_PASSWORD); + break; + case ID_INCOMPATIBLE_PROTOCOL_VERSION: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_INCOMPATIBLE_PROTOCOL); + break; + case ID_IP_RECENTLY_CONNECTED: + pluginList[i]->OnFailedConnectionAttempt(packet, FCAR_IP_RECENTLY_CONNECTED); + break; + } + } +} + +void RakPeer::FillIPList(void) +{ + if (ipList[0]!=UNASSIGNED_SYSTEM_ADDRESS) + return; + + // Fill out ipList structure + RakNetSocket2::GetMyIP( ipList ); + + // Sort the addresses from lowest to highest + int startingIdx = 0; + while (startingIdx < MAXIMUM_NUMBER_OF_INTERNAL_IDS-1 && ipList[startingIdx] != UNASSIGNED_SYSTEM_ADDRESS) + { + int lowestIdx = startingIdx; + for (int curIdx = startingIdx + 1; curIdx < MAXIMUM_NUMBER_OF_INTERNAL_IDS-1 && ipList[curIdx] != UNASSIGNED_SYSTEM_ADDRESS; curIdx++ ) + { + if (ipList[curIdx] < ipList[startingIdx]) + { + lowestIdx = curIdx; + } + } + if (startingIdx != lowestIdx) + { + SystemAddress temp = ipList[startingIdx]; + ipList[startingIdx] = ipList[lowestIdx]; + ipList[lowestIdx] = temp; + } + ++startingIdx; + } +} + + +// #if defined(RMO_NEW_UNDEF_ALLOCATING_QUEUE) +// #pragma pop_macro("new") +// #undef RMO_NEW_UNDEF_ALLOCATING_QUEUE +// #endif + From 460ebb6e3212fb84e2d46a61ea4464e5ac4c0fa5 Mon Sep 17 00:00:00 2001 From: Segfault <5221072+Segfaultd@users.noreply.github.com> Date: Sun, 18 Oct 2026 13:36:16 +0200 Subject: [PATCH 5/8] test(voice): pin every network-thread hook guarantee with a test Adds tests for packet ownership (consumed and continued messages are freed, a kept one stays allocated until the plugin frees it), a sender seen by the hook before the application sees it connect, the relay marker being cleared after each call, the last of several in-relay SetRelayHost calls winning, relay-host state being visible through decoder channels (a cleared host decodes again, a host never decodes, even a straggler), and two threaded stress tests: attach/detach while messages flow, and toggling the relay while frames arrive. Every changed line was checked by mutation: each of 28 deliberate breaks makes at least one test fail, hang or crash. Documents that the hook can see a sender before the application has processed its connection, and must never wait on a thread that may be inside DetachPlugin(), Shutdown() or SetRelayHost(). --- Source/include/mafianet/plugin_interface2.h | 5 +- Source/include/mafianet/voice.h | 5 +- .../NetworkThreadReceiveLiveTests.cpp | 32 +++++ .../RakVoiceNetworkThreadRelayLiveTests.cpp | 48 ++++++++ Tests/Unit/NetworkThreadReceiveTests.cpp | 85 ++++++++++++++ .../Unit/RakVoiceNetworkThreadRelayTests.cpp | 111 +++++++++++++++++- 6 files changed, 283 insertions(+), 3 deletions(-) diff --git a/Source/include/mafianet/plugin_interface2.h b/Source/include/mafianet/plugin_interface2.h index d85332a6a..c7acce9cf 100644 --- a/Source/include/mafianet/plugin_interface2.h +++ b/Source/include/mafianet/plugin_interface2.h @@ -146,7 +146,10 @@ class RAK_DLL_EXPORT PluginInterface2 /// callbacks must be synchronized. RakPeer::Send() is safe to call from here; AttachPlugin() and /// DetachPlugin() do nothing when called from here, and Shutdown() must not be called from here. /// Not called once Shutdown() has begun calling OnRakPeerShutdown(). ID_TIMESTAMP payloads arrive - /// unshifted. + /// unshifted. A sender's messages can arrive here before the user thread has processed its + /// connection notification, so do not assume the application already knows the sender. + /// DetachPlugin() and Shutdown() wait for a call in progress, so this must never wait on a + /// thread that may be inside either of them. /// \param[in] packet The message. RR_STOP_PROCESSING_AND_DEALLOCATE frees it, RR_STOP_PROCESSING /// hands it to the plugin (free it with RakPeer::DeallocatePacket), and /// RR_CONTINUE_PROCESSING queues it for Receive() as usual. diff --git a/Source/include/mafianet/voice.h b/Source/include/mafianet/voice.h index 941768d38..cd9149847 100644 --- a/Source/include/mafianet/voice.h +++ b/Source/include/mafianet/voice.h @@ -316,7 +316,10 @@ class RAK_DLL_EXPORT RakVoice : public PluginInterface2 /// \a relay on RakPeer's network update thread and freed afterwards, so frames are forwarded /// even while the application's main thread is busy, and never reach Receive(). /// \a relay typically picks recipients and calls RelayFrame(), which is safe there; it must - /// not keep the packet, and whatever it reads must be safe to read from that thread. + /// not keep the packet, and whatever it reads must be safe to read from that thread. It can see + /// a sender before the application has processed that sender's connection, and must never + /// wait on a thread that may be inside SetRelayHost(), DetachPlugin() or Shutdown(), which + /// wait for it. /// Pass nullptr to stop being a relay host. Once this returns, the previous \a relay is not /// running and will not be called again. Called from inside \a relay, the change takes /// effect as soon as that call returns. diff --git a/Tests/Integration/NetworkThreadReceiveLiveTests.cpp b/Tests/Integration/NetworkThreadReceiveLiveTests.cpp index b6cd824d8..9148fe8c4 100644 --- a/Tests/Integration/NetworkThreadReceiveLiveTests.cpp +++ b/Tests/Integration/NetworkThreadReceiveLiveTests.cpp @@ -192,3 +192,35 @@ TEST_F(NetworkThreadReceiveLive, HookRunsAgainAfterARestart) EXPECT_GT(probe.calls.load(), before); pair.server->DetachPlugin(&probe); } + +TEST_F(NetworkThreadReceiveLive, AttachAndDetachWhileMessagesFlowIsSafe) +{ + // The user thread attaches and detaches while the update thread dispatches. + // Nothing may crash or be called after its final detach; TSan checks the locking. + BlockingProbe probe; + LivePair pair; + ASSERT_TRUE(pair.Start()); + + std::atomic flooding {true}; + std::thread flooder([&] { + while (flooding.load()) + { + pair.Send(); + RakSleep(1); + } + }); + for (int i = 0; i < 300; ++i) + { + pair.server->AttachPlugin(&probe); + RakSleep(1); + pair.server->DetachPlugin(&probe); + } + const int callsAtLastDetach = probe.calls.load(); + RakSleep(200); + flooding = false; + flooder.join(); + + EXPECT_GT(callsAtLastDetach, 0); + EXPECT_EQ(callsAtLastDetach, probe.calls.load()); + EXPECT_EQ(nullptr, probe.GetRakPeerInterface()); +} diff --git a/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp b/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp index 836f5ab98..2c2c0df8e 100644 --- a/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp +++ b/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp @@ -188,3 +188,51 @@ TEST_F(RakVoiceNetworkThreadRelayLive, ClearingTheRelayWaitsForOneThatIsMidCall) EXPECT_TRUE(cleared.load()); server->DetachPlugin(&voice); } + +TEST_F(RakVoiceNetworkThreadRelayLive, TogglingTheRelayWhileFramesArriveIsSafe) +{ + // One thread plays the network thread, another toggles the relay as fast as it + // can. The hook must never call an emptied relay (std::bad_function_call) or see a + // half-swapped one; under TSan this also checks the state is properly guarded. + RakVoice voice; + std::atomic relayed {0}; + std::atomic threw {false}; + std::atomic done {false}; + + std::thread network([&] { + std::vector frame = RelayFrameFrom(RakNetGUID(7), 1); + Packet p {}; + p.data = frame.data(); + p.length = (unsigned int) frame.size(); + try + { + for (int i = 0; i < 200000; ++i) + voice.OnReceiveNetworkThread(&p); + } + catch (...) + { + threw = true; + } + done = true; + }); + + int toggles = 0; + while (!done.load()) + { + voice.SetRelayHost([&](Packet *) { ++relayed; }); + voice.SetRelayHost(nullptr); + ++toggles; + } + network.join(); + EXPECT_FALSE(threw.load()); + EXPECT_GT(toggles, 0); + + voice.SetRelayHost(nullptr); + const int after = relayed.load(); + std::vector frame = RelayFrameFrom(RakNetGUID(7), 1); + Packet p {}; + p.data = frame.data(); + p.length = (unsigned int) frame.size(); + EXPECT_EQ(RR_CONTINUE_PROCESSING, voice.OnReceiveNetworkThread(&p)); + EXPECT_EQ(after, relayed.load()); +} diff --git a/Tests/Unit/NetworkThreadReceiveTests.cpp b/Tests/Unit/NetworkThreadReceiveTests.cpp index e848ca03e..92d984dc3 100644 --- a/Tests/Unit/NetworkThreadReceiveTests.cpp +++ b/Tests/Unit/NetworkThreadReceiveTests.cpp @@ -391,3 +391,88 @@ TEST(NetworkThreadReceive, AttachFromInsideTheHookIsIgnored) EXPECT_EQ(nullptr, attached.GetRakPeerInterface()); p.server->DetachPlugin(&attacher); } + +namespace +{ + // RakPeer::packetsOutstanding counts packets allocated and not yet freed. Read + // through a pointer-to-member formed in a derived class, which is how a protected + // member of another object may legally be reached. + struct PacketCount : RakPeer + { + static unsigned int Outstanding(const RakPeer *peer) { return peer->*(&PacketCount::packetsOutstanding); } + }; +} + +TEST(NetworkThreadReceive, ConsumedMessagesAreFreed) +{ + Pair p; + NetworkThreadProbe probe; + p.server->AttachPlugin(&probe); + ASSERT_TRUE(p.Start()); + p.net.Step(100); + const unsigned int baseline = PacketCount::Outstanding(p.server); + + for (int i = 0; i < 20; ++i) + SendId(p.client, p.net.AddressOf(p.server), ID_NT_CONSUMED); + ASSERT_TRUE(p.net.RunUntil([&] { return probe.NetworkThreadCount(ID_NT_CONSUMED) == 20; }, 5000)); + p.net.Step(50); + EXPECT_EQ(baseline, PacketCount::Outstanding(p.server)); + p.server->DetachPlugin(&probe); +} + +TEST(NetworkThreadReceive, KeptMessageStaysAllocatedUntilThePluginFreesIt) +{ + Pair p; + NetworkThreadProbe probe; + p.server->AttachPlugin(&probe); + ASSERT_TRUE(p.Start()); + p.net.Step(100); + const unsigned int baseline = PacketCount::Outstanding(p.server); + + SendId(p.client, p.net.AddressOf(p.server), ID_NT_KEPT); + ASSERT_TRUE(p.net.RunUntil([&] { return probe.kept.size() == 1; }, 2000)); + p.net.Step(50); + EXPECT_EQ(baseline + 1, PacketCount::Outstanding(p.server)); + p.server->DeallocatePacket(probe.kept.front()); + EXPECT_EQ(baseline, PacketCount::Outstanding(p.server)); + p.server->DetachPlugin(&probe); +} + +TEST(NetworkThreadReceive, ContinuedMessagesAreFreedByReceiveAsUsual) +{ + Pair p; + NetworkThreadProbe probe; + p.server->AttachPlugin(&probe); + ASSERT_TRUE(p.Start()); + p.net.Step(100); + const unsigned int baseline = PacketCount::Outstanding(p.server); + + for (int i = 0; i < 20; ++i) + SendId(p.client, p.net.AddressOf(p.server), ID_NT_PASSED); + ASSERT_TRUE(p.net.RunUntil([&] { return InboxCount(p.net, p.server, ID_NT_PASSED) == 20; }, 5000)); + EXPECT_EQ(baseline, PacketCount::Outstanding(p.server)); + p.server->DetachPlugin(&probe); +} + +TEST(NetworkThreadReceive, HookCanSeeASenderBeforeTheApplicationSeesItConnect) +{ + // The connection notification waits in the Receive() queue, but the sender's + // messages are offered on the network thread as they arrive, so a hook must not + // assume the application already knows the sender. + Pair p; + NetworkThreadProbe probe; + p.server->AttachPlugin(&probe); + p.net.SetReceiveEnabled(p.server, false); + char host[64]; + p.net.AddressOf(p.server).ToString(false, host, sizeof(host)); + ASSERT_EQ(CONNECTION_ATTEMPT_STARTED, p.client->Connect(host, p.net.AddressOf(p.server).GetPort(), nullptr, 0)); + ASSERT_TRUE(p.net.RunUntilReceived(p.client, ID_CONNECTION_REQUEST_ACCEPTED, 5000)); + + SendId(p.client, p.net.AddressOf(p.server), ID_NT_CONSUMED); + ASSERT_TRUE(p.net.RunUntil([&] { return probe.NetworkThreadCount(ID_NT_CONSUMED) == 1; }, 2000)); + EXPECT_FALSE(p.net.Received(p.server, ID_NEW_INCOMING_CONNECTION)); + + p.net.SetReceiveEnabled(p.server, true); + EXPECT_TRUE(p.net.RunUntilReceived(p.server, ID_NEW_INCOMING_CONNECTION, 2000)); + p.server->DetachPlugin(&probe); +} diff --git a/Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp b/Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp index 6bc4f39ad..1c13eafcb 100644 --- a/Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp +++ b/Tests/Unit/RakVoiceNetworkThreadRelayTests.cpp @@ -236,8 +236,9 @@ TEST(RakVoiceNetworkThreadRelay, RelayCanReplaceItselfFromInside) Packet p = View(frame, Guid(7)); voice.OnReceiveNetworkThread(&p); voice.OnReceiveNetworkThread(&p); + voice.OnReceiveNetworkThread(&p); EXPECT_EQ(1, first); - EXPECT_EQ(1, second); + EXPECT_EQ(2, second); // the swap happened once, and the new relay stays in place } TEST(RakVoiceNetworkThreadRelay, RelayHostDropsAStragglerFrameThatReachedReceive) @@ -321,3 +322,111 @@ TEST(RakVoiceNetworkThreadRelay, EveryFrameOfABurstIsRelayedInOrder) for (unsigned short i = 0; i < kFrames; ++i) EXPECT_EQ(i + 1, sequences[i]); } + +TEST(RakVoiceNetworkThreadRelay, SetRelayHostAfterARelayCallTakesEffectAtOnce) +{ + // The relay's thread marker must be cleared when it returns, or a later + // SetRelayHost from that same thread would be parked as if made from inside. + RakVoice voice; + int first = 0, second = 0; + voice.SetRelayHost([&](Packet *) { ++first; }); + + std::vector frame = RelayFrameFrom(Guid(7)); + Packet p = View(frame, Guid(7)); + voice.OnReceiveNetworkThread(&p); + voice.SetRelayHost([&](Packet *) { ++second; }); + voice.OnReceiveNetworkThread(&p); + EXPECT_EQ(1, first); + EXPECT_EQ(1, second); + + voice.SetRelayHost(nullptr); + EXPECT_EQ(RR_CONTINUE_PROCESSING, voice.OnReceiveNetworkThread(&p)); +} + +TEST(RakVoiceNetworkThreadRelay, LastSetRelayHostFromInsideWins) +{ + RakVoice voice; + int first = 0, second = 0, third = 0; + voice.SetRelayHost([&](Packet *) { + ++first; + voice.SetRelayHost([&](Packet *) { ++second; }); + voice.SetRelayHost([&](Packet *) { ++third; }); + }); + + std::vector frame = RelayFrameFrom(Guid(7)); + Packet p = View(frame, Guid(7)); + voice.OnReceiveNetworkThread(&p); + voice.OnReceiveNetworkThread(&p); + EXPECT_EQ(1, first); + EXPECT_EQ(0, second); + EXPECT_EQ(1, third); +} + +// --- Relay host vs decoding client, observed through decoder channels ---------- +// +// An initialized RakVoice that is not a relay host decodes relay frames, which opens +// a decoder channel for the frame's origin. A relay host never does. That makes the +// relay-host state visible from outside. + +namespace +{ + bool HasSpeaker(RakVoice &voice, RakNetGUID who) + { + DataStructures::List speakers; + voice.GetActiveSpeakers(speakers); + for (unsigned i = 0; i < speakers.Size(); ++i) + if (speakers[i] == who) + return true; + return false; + } +} + +TEST(RakVoiceNetworkThreadRelay, ClearedRelayHostDecodesRelayFramesAgain) +{ + RelayRig rig; + rig.voice.Init(48000, 960 * sizeof(short)); + ASSERT_TRUE(rig.voice.IsInitialized()); + ASSERT_TRUE(rig.Start()); + rig.voice.SetRelayHost([](Packet *) {}); + rig.voice.SetRelayHost(nullptr); + + rig.Talk(); + ASSERT_TRUE(rig.net.RunUntil([&] { return HasSpeaker(rig.voice, rig.talker->GetMyGUID()); }, 2000)); +} + +TEST(RakVoiceNetworkThreadRelay, RelayHostClearedFromInsideDecodesRelayFramesAgain) +{ + RelayRig rig; + rig.voice.Init(48000, 960 * sizeof(short)); + ASSERT_TRUE(rig.voice.IsInitialized()); + ASSERT_TRUE(rig.Start()); + std::atomic relayed {0}; + rig.voice.SetRelayHost([&](Packet *) { + ++relayed; + rig.voice.SetRelayHost(nullptr); + }); + + rig.Talk(); + ASSERT_TRUE(rig.net.RunUntil([&] { return relayed.load() == 1; }, 2000)); + EXPECT_FALSE(HasSpeaker(rig.voice, rig.talker->GetMyGUID())); + rig.Talk(); + ASSERT_TRUE(rig.net.RunUntil([&] { return HasSpeaker(rig.voice, rig.talker->GetMyGUID()); }, 2000)); + EXPECT_EQ(1, relayed.load()); +} + +TEST(RakVoiceNetworkThreadRelay, RelayHostNeverDecodesEvenAStraggler) +{ + RelayRig rig; + rig.voice.Init(48000, 960 * sizeof(short)); + ASSERT_TRUE(rig.voice.IsInitialized()); + ASSERT_TRUE(rig.Start()); + rig.net.SetReceiveEnabled(rig.server, false); + rig.Talk(); + rig.net.Step(200); // queued for Receive() before this peer became a relay host + + rig.voice.SetRelayHost([](Packet *) {}); + rig.net.SetReceiveEnabled(rig.server, true); + rig.Talk(); + rig.net.Step(200); + EXPECT_FALSE(HasSpeaker(rig.voice, rig.talker->GetMyGUID())); +} From 1dfa519e9d929325134e748bf5034a0152657d99 Mon Sep 17 00:00:00 2001 From: Segfault <5221072+Segfaultd@users.noreply.github.com> Date: Sun, 18 Oct 2026 15:17:33 +0200 Subject: [PATCH 6/8] test(voice): check that a client never waits on the relay lock RakVoice skips the relay lock when it is not a relay host, and a relay host takes it only for relay frames, so ordinary traffic is never serialized behind a relay call in progress. Two tests hold the lock on another thread and check that those calls still return, with a control that a relay frame on a relay host does wait. Removing the not-a-relay-host check now fails the first test. --- .../RakVoiceNetworkThreadRelayLiveTests.cpp | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp b/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp index 2c2c0df8e..7972270b1 100644 --- a/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp +++ b/Tests/Integration/RakVoiceNetworkThreadRelayLiveTests.cpp @@ -236,3 +236,64 @@ TEST_F(RakVoiceNetworkThreadRelayLive, TogglingTheRelayWhileFramesArriveIsSafe) EXPECT_EQ(RR_CONTINUE_PROCESSING, voice.OnReceiveNetworkThread(&p)); EXPECT_EQ(after, relayed.load()); } + +namespace +{ + // Reaches the relay lock so a test can hold it from another thread, the way a + // relay call in progress on the network thread would. + class RelayLockProbe : public RakVoice + { + public: + void LockRelay() { relayFrameHandlerMutex.Lock(); } + void UnlockRelay() { relayFrameHandlerMutex.Unlock(); } + }; + + // Runs voice.OnReceiveNetworkThread(frame) while another thread holds the relay + // lock. Returns whether the call finished without waiting for that lock. + bool FinishesWhileTheRelayLockIsHeld(RelayLockProbe &voice, std::vector frame) + { + std::promise locked, release; + std::thread holder([&] { + voice.LockRelay(); + locked.set_value(); + release.get_future().wait(); + voice.UnlockRelay(); + }); + locked.get_future().wait(); + + std::promise finished; + std::future done = finished.get_future(); + std::thread caller([&] { + Packet p {}; + p.data = frame.data(); + p.length = (unsigned int) frame.size(); + voice.OnReceiveNetworkThread(&p); + finished.set_value(); + }); + const bool unblocked = done.wait_for(std::chrono::seconds(2)) == std::future_status::ready; + release.set_value(); + holder.join(); + caller.join(); + return unblocked; + } +} + +TEST_F(RakVoiceNetworkThreadRelayLive, AClientNeverWaitsOnTheRelayLock) +{ + // A peer that is not a relay host must not serialize its traffic behind the relay + // lock: every message it receives goes through this hook. + RelayLockProbe voice; + EXPECT_TRUE(FinishesWhileTheRelayLockIsHeld(voice, RelayFrameFrom(RakNetGUID(7), 1))); +} + +TEST_F(RakVoiceNetworkThreadRelayLive, ARelayHostOnlyWaitsOnTheRelayLockForRelayFrames) +{ + RelayLockProbe voice; + voice.SetRelayHost([](Packet *) {}); + std::vector other {ID_RAKVOICE_DATA, 1, 2, 3}; + EXPECT_TRUE(FinishesWhileTheRelayLockIsHeld(voice, other)); + // Control: a relay frame on a relay host does wait, which is what proves the two + // checks above are exercising the lock at all. + EXPECT_FALSE(FinishesWhileTheRelayLockIsHeld(voice, RelayFrameFrom(RakNetGUID(7), 1))); + voice.SetRelayHost(nullptr); +} From ce2c422697398c64a541320835df2e418b540448 Mon Sep 17 00:00:00 2001 From: Segfault <5221072+Segfaultd@users.noreply.github.com> Date: Sun, 18 Oct 2026 15:32:46 +0200 Subject: [PATCH 7/8] fix(peer): re-arm the network-thread hook when Shutdown() ends Startup() reset the hook's stop flag, which made the constructor's initial value dead: nothing could ever read it. The reset now happens at the end of Shutdown(), once the update thread has exited, so the constructor's value is the one the first Startup() runs with and a test fails if it is wrong. Behaviour is otherwise unchanged: the hook still stops before OnRakPeerShutdown and runs again after a restart. --- Source/include/mafianet/peer.h | 3 ++- Source/src/peer.cpp | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/include/mafianet/peer.h b/Source/include/mafianet/peer.h index d42bf6409..94f08ff98 100644 --- a/Source/include/mafianet/peer.h +++ b/Source/include/mafianet/peer.h @@ -1003,7 +1003,8 @@ class RAK_DLL_EXPORT RakPeer : public RakPeerInterface, public RNS2EventHandler // on it are ignored: they would deadlock on the lock that thread holds, and they also change // the plugin lists Receive() walks on the user thread. std::atomic networkThreadDispatcher; - // Set once Shutdown() starts telling plugins, cleared by Startup(). Guarded by the mutex above. + // Set once Shutdown() starts telling plugins, cleared again once its update thread has exited. + // Starts false, so the first Startup() offers messages. Guarded by the mutex above. bool networkThreadReceiveStopped; bool IsInsideNetworkThreadReceive(void) const; void SetNetworkThreadReceiveStopped(bool stopped); diff --git a/Source/src/peer.cpp b/Source/src/peer.cpp index 3d6ca5aea..62733002b 100644 --- a/Source/src/peer.cpp +++ b/Source/src/peer.cpp @@ -414,8 +414,6 @@ StartupResult RakPeer::Startup( unsigned int maxConnections, SocketDescriptor *s if (IsActive()) return RAKNET_ALREADY_STARTED; - SetNetworkThreadReceiveStopped(false); - // If getting the guid failed in the constructor, try again if (myGuid.g==0) { @@ -1100,6 +1098,11 @@ void RakPeer::Shutdown( unsigned int blockDuration, unsigned char orderingChanne RakSleep(15); } + // The update thread has exited, so nothing can be in or enter the hook: re-arm it for the next + // Startup(). Re-arming here rather than in Startup() means the constructor's value is the one + // the first Startup() runs with. + SetNetworkThreadReceiveStopped(false); + activeSystemListSize = 0; /* From 4dc7f918b93a75c767cea659841c36288ea7e0e6 Mon Sep 17 00:00:00 2001 From: Segfault <5221072+Segfaultd@users.noreply.github.com> Date: Sun, 18 Oct 2026 16:42:35 +0200 Subject: [PATCH 8/8] test(peer): build a peer over poisoned memory to pin the stop flag's init Without an initializer, the hook's stop flag held whatever the allocator left there, so a missing initialization failed the tests on some builds and passed on others. SimulatedNetwork::AddPeerInStorage constructs a peer in caller-owned memory; a test fills that memory with set bits first, so a missing initialization fails every time. --- Tests/Support/SimulatedNetwork.cpp | 21 ++++++++++++++++-- Tests/Support/SimulatedNetwork.h | 7 ++++++ Tests/Unit/NetworkThreadReceiveTests.cpp | 27 ++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/Tests/Support/SimulatedNetwork.cpp b/Tests/Support/SimulatedNetwork.cpp index ee9cac15d..4a85b8ec5 100644 --- a/Tests/Support/SimulatedNetwork.cpp +++ b/Tests/Support/SimulatedNetwork.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "mafianet/assert.h" #include "mafianet/message_identifiers.h" @@ -61,7 +62,10 @@ namespace MafiaNet for (RakPeer *peer : peers) { peer->Shutdown(0); - RakPeerInterface::DestroyInstance(peer); + if (peersInStorage.count(peer) != 0) + peer->~RakPeer(); + else + RakPeerInterface::DestroyInstance(peer); } SetTimeSourceOverride(nullptr); g_networkAlive = false; @@ -79,7 +83,20 @@ namespace MafiaNet RakPeer *SimulatedNetwork::AddPeer(unsigned int maxConnections, unsigned short maxIncoming, unsigned short port) { - RakPeer *peer = static_cast(RakPeerInterface::GetInstance()); + return StartPeer(static_cast(RakPeerInterface::GetInstance()), maxConnections, maxIncoming, port); + } + + RakPeer *SimulatedNetwork::AddPeerInStorage(void *storage, unsigned int maxConnections, unsigned short maxIncoming, + unsigned short port) + { + RakPeer *peer = new (storage) RakPeer(); + peersInStorage.insert(peer); + return StartPeer(peer, maxConnections, maxIncoming, port); + } + + RakPeer *SimulatedNetwork::StartPeer(RakPeer *peer, unsigned int maxConnections, unsigned short maxIncoming, + unsigned short port) + { peer->SetSocketFactory(this); peer->SetManualUpdate(true); diff --git a/Tests/Support/SimulatedNetwork.h b/Tests/Support/SimulatedNetwork.h index 5c26a1bd8..b67531c16 100644 --- a/Tests/Support/SimulatedNetwork.h +++ b/Tests/Support/SimulatedNetwork.h @@ -67,6 +67,11 @@ namespace MafiaNet // A started peer bound to 10.0.0.: (port 0 picks one). RakPeer *AddPeer(unsigned int maxConnections, unsigned short maxIncoming = 0, unsigned short port = 0); + // The same, but the peer is constructed in caller-owned storage (at least + // sizeof(RakPeer) bytes, suitably aligned) that must outlive this network. + // Lets a test pre-fill that memory to prove the constructor sets what it must. + RakPeer *AddPeerInStorage(void *storage, unsigned int maxConnections, unsigned short maxIncoming = 0, + unsigned short port = 0); SystemAddress AddressOf(const RakPeer *peer) const; // Rules apply per direction; unset links use the default rule. @@ -127,11 +132,13 @@ namespace MafiaNet void Enqueue(const SystemAddress &from, const SystemAddress &to, const char *data, int length); void DeliverDue(); + RakPeer *StartPeer(RakPeer *peer, unsigned int maxConnections, unsigned short maxIncoming, unsigned short port); const LinkRule &RuleFor(const SystemAddress &from, const SystemAddress &to) const; uint32_t NextRandom(); static TimeUS VirtualClock(); std::vector peers; + std::set peersInStorage; std::map > inboxes; std::set receiveDisabled; std::map sockets; diff --git a/Tests/Unit/NetworkThreadReceiveTests.cpp b/Tests/Unit/NetworkThreadReceiveTests.cpp index 92d984dc3..91701c16a 100644 --- a/Tests/Unit/NetworkThreadReceiveTests.cpp +++ b/Tests/Unit/NetworkThreadReceiveTests.cpp @@ -13,6 +13,8 @@ #include +#include +#include #include #include "SimulatedNetwork.h" @@ -476,3 +478,28 @@ TEST(NetworkThreadReceive, HookCanSeeASenderBeforeTheApplicationSeesItConnect) EXPECT_TRUE(p.net.RunUntilReceived(p.server, ID_NEW_INCOMING_CONNECTION, 2000)); p.server->DetachPlugin(&probe); } + +TEST(NetworkThreadReceive, FirstStartupOffersMessagesWhateverTheMemoryHeldBefore) +{ + // The constructor must clear the hook's stop flag: the first Startup() runs with + // that value. Building the peer over memory full of set bits makes a missing + // initialisation fail every time, instead of only when the allocator leaves junk. + struct Storage + { + void *bytes = ::operator new(sizeof(RakPeer)); + Storage() { std::memset(bytes, 0xFF, sizeof(RakPeer)); } + ~Storage() { ::operator delete(bytes); } + } storage; // declared first: it must outlive the network that destroys the peer + + SimulatedNetwork net; + RakPeer *server = net.AddPeerInStorage(storage.bytes, 2, 2); + RakPeer *client = net.AddPeer(1); + NetworkThreadProbe probe; + server->AttachPlugin(&probe); + ASSERT_TRUE(net.Connect(client, server)); + + net.SetReceiveEnabled(server, false); + SendId(client, net.AddressOf(server), ID_NT_CONSUMED); + EXPECT_TRUE(net.RunUntil([&] { return probe.NetworkThreadCount(ID_NT_CONSUMED) == 1; }, 2000)); + server->DetachPlugin(&probe); +}