mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-08-08 11:44:11 +00:00
Public release of the DLU server code!
Have fun!
This commit is contained in:
822
thirdparty/raknet/Source/NatPunchthrough.cpp
vendored
Normal file
822
thirdparty/raknet/Source/NatPunchthrough.cpp
vendored
Normal file
@@ -0,0 +1,822 @@
|
||||
#include "NatPunchthrough.h"
|
||||
#include "GetTime.h"
|
||||
#include "RakPeerInterface.h"
|
||||
#include "BitStream.h"
|
||||
#include "MessageIdentifiers.h"
|
||||
#include "RakAssert.h"
|
||||
#include "FormatString.h"
|
||||
|
||||
#include <stdio.h>
|
||||
using namespace RakNet;
|
||||
|
||||
// Number is arbitrary, but you want a long enough interval to get a good sample, but short enough not to annoy the user.
|
||||
static const int PING_INTERVAL=100;
|
||||
// Fill up the ping buffer
|
||||
static const int PING_COUNT=PING_TIMES_ARRAY_SIZE;
|
||||
// What multiple to multiply the ping by to figure out how long to wait before sending the start time.
|
||||
// Should be slightly larger than the ping so the last ping arrives
|
||||
static const float SEND_TIMESTAMP_DELAY_PING_MULTIPLE=1.5f;
|
||||
// What multiple to multiply the ping by to figure out when to send the NAT punchthrough request
|
||||
// Should be large enough to make sure both systems get the message in the future, even if it needs to be resent due to packetloss.
|
||||
// Too large is bad because the user has to wait and it makes it more likely our estimated ping is wrong
|
||||
static const float SEND_PUNCHTHROUGH_DELAY_PING_MULTIPLE=3.0f;
|
||||
static const int TTL_HOPS=3;
|
||||
|
||||
// As I add more out of band messages this will be moved elsewhere
|
||||
static const char ID_NAT_ADVERTISE_RECIPIENT_PORT=0;
|
||||
|
||||
static const int RECIPIENT_OFFLINE_MAX_COUNT=10;
|
||||
// See
|
||||
// static const int MAX_OPEN_CONNECTION_REQUESTS=4;
|
||||
// static const int TIME_BETWEEN_OPEN_CONNECTION_REQUESTS=500;
|
||||
// In RakPeer.cpp
|
||||
static const int RECIPIENT_OFFLINE_MESSAGE_INTERVAL=4000;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( push )
|
||||
#endif
|
||||
|
||||
void NatPunchthroughLogger::OnMessage(const char *str)
|
||||
{
|
||||
printf("%s", str);
|
||||
}
|
||||
|
||||
NatPunchthrough::NatPunchthrough()
|
||||
{
|
||||
allowFacilitation=true;
|
||||
rakPeer=0;
|
||||
log=0;
|
||||
}
|
||||
NatPunchthrough::~NatPunchthrough()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
void NatPunchthrough::FacilitateConnections(bool allow)
|
||||
{
|
||||
allowFacilitation=allow;
|
||||
}
|
||||
bool NatPunchthrough::Connect(const char* destination, unsigned short remotePort, const char *passwordData, int passwordDataLength, SystemAddress facilitator)
|
||||
{
|
||||
SystemAddress systemAddress;
|
||||
systemAddress.SetBinaryAddress(destination);
|
||||
systemAddress.port=remotePort;
|
||||
return Connect(systemAddress, passwordData, passwordDataLength, facilitator);
|
||||
}
|
||||
bool NatPunchthrough::Connect(SystemAddress destination, const char *passwordData, int passwordDataLength, SystemAddress facilitator)
|
||||
{
|
||||
if (rakPeer->IsConnected(facilitator)==false)
|
||||
return false;
|
||||
|
||||
RakNet::BitStream outBitstream;
|
||||
outBitstream.Write((MessageID)ID_NAT_PUNCHTHROUGH_REQUEST);
|
||||
// http://www.bford.info/pub/net/p2pnat/
|
||||
// A few poorly behaved NATs are known to scan the body of UDP datagrams for 4-byte fields that look like IP addresses, and translate them as they would the IP address fields in the IP header.
|
||||
outBitstream.Write(true);
|
||||
outBitstream.Write(destination);
|
||||
|
||||
// Remember this connection request
|
||||
NatPunchthrough::ConnectionRequest *connectionRequest = new NatPunchthrough::ConnectionRequest;
|
||||
connectionRequest->receiverPublic=destination;
|
||||
connectionRequest->facilitator=facilitator;
|
||||
if (passwordDataLength)
|
||||
{
|
||||
connectionRequest->passwordData = (char*) rakMalloc( passwordDataLength );
|
||||
memcpy(connectionRequest->passwordData, passwordData, passwordDataLength);
|
||||
}
|
||||
else
|
||||
connectionRequest->passwordData = 0;
|
||||
connectionRequest->senderPublic=UNASSIGNED_SYSTEM_ADDRESS;
|
||||
connectionRequest->passwordDataLength=passwordDataLength;
|
||||
connectionRequest->facilitator=facilitator;
|
||||
connectionRequest->advertisedPort=0;
|
||||
connectionRequest->nextActionTime=0;
|
||||
connectionRequest->attemptedConnection=false;
|
||||
connectionRequest->facilitatingConnection=false;
|
||||
connectionRequest->timeoutTime=RakNet::GetTime()+30000;
|
||||
connectionRequest->pingCount=0;
|
||||
connectionRequestList.Insert(connectionRequest);
|
||||
|
||||
rakPeer->Send(&outBitstream, HIGH_PRIORITY, RELIABLE, 0, facilitator, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
void NatPunchthrough::Clear(void)
|
||||
{
|
||||
unsigned i;
|
||||
for (i=0; i < connectionRequestList.Size(); i++)
|
||||
{
|
||||
rakFree(connectionRequestList[i]->passwordData);
|
||||
delete connectionRequestList[i];
|
||||
}
|
||||
connectionRequestList.Clear();
|
||||
}
|
||||
void NatPunchthrough::OnAttach(RakPeerInterface *peer)
|
||||
{
|
||||
rakPeer=peer;
|
||||
}
|
||||
void NatPunchthrough::Update(RakPeerInterface *peer)
|
||||
{
|
||||
(void) peer;
|
||||
if (connectionRequestList.Size())
|
||||
{
|
||||
RakNetTime time = RakNet::GetTime();
|
||||
RakNet::BitStream outBitstream;
|
||||
unsigned i;
|
||||
i=0;
|
||||
while (i < connectionRequestList.Size())
|
||||
{
|
||||
// Remove old connection requests that get no answer
|
||||
if (connectionRequestList[i]->timeoutTime < time)
|
||||
{
|
||||
rakFree(connectionRequestList[i]->passwordData);
|
||||
delete connectionRequestList[i];
|
||||
connectionRequestList.RemoveAtIndex(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we are a facilitator, do pings
|
||||
if (connectionRequestList[i]->facilitatingConnection)
|
||||
{
|
||||
if (time >= connectionRequestList[i]->nextActionTime )
|
||||
{
|
||||
if (connectionRequestList[i]->pingCount < PING_COUNT)
|
||||
{
|
||||
// Ping
|
||||
connectionRequestList[i]->pingCount++;
|
||||
rakPeer->Ping(connectionRequestList[i]->receiverPublic);
|
||||
rakPeer->Ping(connectionRequestList[i]->senderPublic);
|
||||
connectionRequestList[i]->nextActionTime=time+PING_INTERVAL;
|
||||
}
|
||||
else if (connectionRequestList[i]->pingCount == PING_COUNT)
|
||||
{
|
||||
// Done pinging. Wait till the next stage
|
||||
int receiverPing, senderPing;
|
||||
receiverPing=rakPeer->GetAveragePing(connectionRequestList[i]->receiverPublic);
|
||||
senderPing=rakPeer->GetAveragePing(connectionRequestList[i]->senderPublic);
|
||||
if (receiverPing > senderPing)
|
||||
connectionRequestList[i]->nextActionTime=(RakNetTime)(receiverPing*SEND_TIMESTAMP_DELAY_PING_MULTIPLE);
|
||||
else
|
||||
connectionRequestList[i]->nextActionTime=(RakNetTime)(senderPing*SEND_TIMESTAMP_DELAY_PING_MULTIPLE);
|
||||
connectionRequestList[i]->pingCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send the timestamped message to both systems so they send datagrams to each other simultaneously
|
||||
int receiverPing, senderPing;
|
||||
RakNetTime delayTime;
|
||||
receiverPing=rakPeer->GetAveragePing(connectionRequestList[i]->receiverPublic);
|
||||
senderPing=rakPeer->GetAveragePing(connectionRequestList[i]->senderPublic);
|
||||
if (receiverPing > senderPing)
|
||||
delayTime=(RakNetTime)(receiverPing*SEND_PUNCHTHROUGH_DELAY_PING_MULTIPLE);
|
||||
else
|
||||
delayTime=(RakNetTime)(senderPing*SEND_PUNCHTHROUGH_DELAY_PING_MULTIPLE);
|
||||
|
||||
if (delayTime < 500)
|
||||
delayTime=500;
|
||||
|
||||
outBitstream.Reset();
|
||||
outBitstream.Write((MessageID)ID_TIMESTAMP);
|
||||
outBitstream.Write(time+delayTime);
|
||||
outBitstream.Write((MessageID)ID_NAT_CONNECT_AT_TIME);
|
||||
// A few poorly behaved NATs are known to scan the body of UDP datagrams for 4-byte fields that look like IP addresses, and translate them as they would the IP address fields in the IP header.
|
||||
outBitstream.Write(true);
|
||||
outBitstream.Write(rakPeer->GetInternalID(connectionRequestList[i]->receiverPublic));
|
||||
rakPeer->Send(&outBitstream, SYSTEM_PRIORITY, RELIABLE, 0, connectionRequestList[i]->senderPublic, false);
|
||||
|
||||
outBitstream.Reset();
|
||||
outBitstream.Write((MessageID)ID_TIMESTAMP);
|
||||
outBitstream.Write(time+delayTime);
|
||||
outBitstream.Write((MessageID)ID_NAT_SEND_OFFLINE_MESSAGE_AT_TIME);
|
||||
// A few poorly behaved NATs are known to scan the body of UDP datagrams for 4-byte fields that look like IP addresses, and translate them as they would the IP address fields in the IP header.
|
||||
outBitstream.Write(true);
|
||||
outBitstream.Write(connectionRequestList[i]->senderPublic);
|
||||
outBitstream.Write(rakPeer->GetInternalID(connectionRequestList[i]->senderPublic));
|
||||
rakPeer->Send(&outBitstream, SYSTEM_PRIORITY, RELIABLE, 0, connectionRequestList[i]->receiverPublic, false);
|
||||
|
||||
delete [] connectionRequestList[i]->passwordData;
|
||||
delete connectionRequestList[i];
|
||||
connectionRequestList.RemoveAtIndex(i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Else not a facilitator. If nextActionTime is non-zero, connect or send the offline message
|
||||
else if (connectionRequestList[i]->nextActionTime && time >= connectionRequestList[i]->nextActionTime && connectionRequestList[i]->attemptedConnection==false)
|
||||
{
|
||||
ConnectionRequest *connectionRequest = connectionRequestList[i];
|
||||
DataStructures::List<SystemAddress> fallbackAddresses;
|
||||
// unsigned fallbackIndex;
|
||||
RakNet::BitStream oob;
|
||||
if (connectionRequest->receiverPublic!=UNASSIGNED_SYSTEM_ADDRESS)
|
||||
{
|
||||
// fallbackAddresses.Clear();
|
||||
// connectionRequest->GetAddressList(peer, fallbackAddresses, connectionRequest->senderPublic, connectionRequest->senderPrivate, true);
|
||||
// for (unsigned fallbackIndex=0; fallbackIndex < fallbackAddresses.Size(); fallbackIndex++)
|
||||
// peer->Ping(fallbackAddresses[fallbackIndex].ToString(false),fallbackAddresses[fallbackIndex].port,false);
|
||||
|
||||
// Connect to this system.
|
||||
LogOut(FormatString("Connecting to public address %s\n", connectionRequest->receiverPublic.ToString(true)));
|
||||
rakPeer->Connect(connectionRequest->receiverPublic.ToString(false), connectionRequest->receiverPublic.port, connectionRequest->passwordData, connectionRequest->passwordDataLength);
|
||||
connectionRequest->attemptedConnection=true;
|
||||
|
||||
/*
|
||||
if (connectionRequestList[i]->receiverPrivate==connectionRequestList[i]->receiverPublic)
|
||||
{
|
||||
// Otherwise done with this connection, as it's a normal connection attempt
|
||||
rakFree(connectionRequestList[i]->passwordData);
|
||||
delete connectionRequestList[i];
|
||||
connectionRequestList.RemoveAtIndex(i);
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
RakAssert(connectionRequestList[i]->senderPublic!=UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
// Send ID_NAT_ADVERTISE_RECIPIENT_PORT to all possible remote ports
|
||||
fallbackAddresses.Clear();
|
||||
connectionRequest->GetAddressList(peer, fallbackAddresses, connectionRequest->senderPublic, connectionRequest->senderPrivate, true);
|
||||
|
||||
/// 04/13/08 I think if I send to all possible fallbacks in order it starts reusing external ports or else assigns them in a random way
|
||||
/// Sending 4X to each port in order did work however.
|
||||
/// Timing matches that of RakPeer's failure notices and retries
|
||||
if ((unsigned int) connectionRequestList[i]->recipientOfflineCount < fallbackAddresses.Size())
|
||||
{
|
||||
unsigned k;
|
||||
//for (k=0; k < 4; k++)
|
||||
for (k=0; k < (unsigned) connectionRequestList[i]->recipientOfflineCount && k < fallbackAddresses.Size(); k++)
|
||||
{
|
||||
oob.Write(ID_NAT_ADVERTISE_RECIPIENT_PORT);
|
||||
oob.Write(rakPeer->GetExternalID(connectionRequest->facilitator));
|
||||
// This is duplicated intentionally, since the first packet is often dropped
|
||||
peer->SendOutOfBand(fallbackAddresses[k].ToString(false),fallbackAddresses[k].port,ID_OUT_OF_BAND_INTERNAL,(const char*) oob.GetData(),oob.GetNumberOfBytesUsed(),0);
|
||||
peer->SendOutOfBand(fallbackAddresses[k].ToString(false),fallbackAddresses[k].port,ID_OUT_OF_BAND_INTERNAL,(const char*) oob.GetData(),oob.GetNumberOfBytesUsed(),0);
|
||||
}
|
||||
|
||||
LogOut(FormatString("Recipient sending ID_OUT_OF_BAND_INTERNAL to %s\n", fallbackAddresses[connectionRequestList[i]->recipientOfflineCount].ToString(true)));
|
||||
}
|
||||
|
||||
/*
|
||||
for (fallbackIndex=0; fallbackIndex < fallbackAddresses.Size(); fallbackIndex++)
|
||||
{
|
||||
// This follows a similar path to the sender connecting to us.
|
||||
// If this message arrives, it lets the sender know our external port.
|
||||
// The sender can then use this port instead of just going down the fallbackAddresses guessing.
|
||||
// This saves us if the recipient is impossible to connect to, but the sender is not.
|
||||
oob.Reset();
|
||||
oob.Write(ID_NAT_ADVERTISE_RECIPIENT_PORT);
|
||||
oob.Write(rakPeer->GetExternalID(connectionRequest->facilitator));
|
||||
peer->SendOutOfBand(fallbackAddresses[fallbackIndex].ToString(false),fallbackAddresses[fallbackIndex].port,ID_OUT_OF_BAND_INTERNAL,(const char*) oob.GetData(),oob.GetNumberOfBytesUsed(),0);
|
||||
|
||||
// bool SendOutOfBand(const char *host, unsigned short remotePort, MessageID header, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex=0 );
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
// LogOut(FormatString("Pinging %s\n", connectionRequest->senderPublic.ToString(true)));
|
||||
|
||||
// rakPeer->Connect(connectionRequestList[i]->senderPublic.ToString(false),connectionRequestList[i]->senderPublic.port,0,0,0);
|
||||
|
||||
/*
|
||||
SystemAddress senderAddrWithFPort;
|
||||
SystemAddress senderAddrWithRPort;
|
||||
SystemAddress senderAddrWithIPort;
|
||||
senderAddrWithFPort=connectionRequestList[i]->senderPublic;
|
||||
senderAddrWithFPort.port=connectionRequestList[i]->facilitator.port;
|
||||
senderAddrWithRPort=connectionRequestList[i]->senderPublic;
|
||||
senderAddrWithRPort.port=rakPeer->GetExternalID(connectionRequestList[i]->facilitator).port;
|
||||
senderAddrWithIPort=connectionRequestList[i]->senderPublic;
|
||||
senderAddrWithIPort.port=rakPeer->GetInternalID().port;
|
||||
*/
|
||||
|
||||
// LogOut(FormatString("Recipient sending TTL %i hops to %s\n", TTL_HOPS, connectionRequestList[i]->senderPublic.ToString()));
|
||||
// rakPeer->SendTTL(connectionRequestList[i]->senderPublic.ToString(false), connectionRequestList[i]->senderPublic.port, false);
|
||||
|
||||
|
||||
// Send offline message to this system, hopefully at the exact same time that system tries to connect to us.
|
||||
// rakPeer->Ping(connectionRequestList[i]->senderPublic.ToString(false), connectionRequestList[i]->senderPublic.port, false);
|
||||
/*
|
||||
rakPeer->Ping(senderAddrWithFPort.ToString(false), senderAddrWithFPort.port, false);
|
||||
rakPeer->Ping(senderAddrWithRPort.ToString(false), senderAddrWithRPort.port, false);
|
||||
rakPeer->Ping(senderAddrWithIPort.ToString(false), senderAddrWithIPort.port, false);
|
||||
*/
|
||||
|
||||
// if (connectionRequestList[i]->senderPrivate!=connectionRequestList[i]->senderPublic)
|
||||
// rakPeer->Ping(connectionRequestList[i]->senderPrivate.ToString(false), connectionRequestList[i]->senderPrivate.port, false);
|
||||
|
||||
// LogOut(FormatString("Recipient sending TTL %i hops to %s\n", TTL_HOPS, connectionRequestList[i]->senderPublic.ToString(false)));
|
||||
// peer->SendTTL(connectionRequestList[i]->senderPublic.ToString(false),connectionRequestList[i]->senderPublic.port, TTL_HOPS+connectionRequestList[i]->recipientOfflineCount, 0);
|
||||
// peer->SendTTL(connectionRequestList[i]->senderPublic.ToString(false),connectionRequestList[i]->senderPublic.port+1, TTL_HOPS+connectionRequestList[i]->recipientOfflineCount, 0);
|
||||
// peer->SendTTL(connectionRequestList[i]->senderPrivate.ToString(false),connectionRequestList[i]->senderPrivate.port, TTL_HOPS+connectionRequestList[i]->recipientOfflineCount, 0);
|
||||
|
||||
connectionRequestList[i]->recipientOfflineCount++;
|
||||
if (connectionRequestList[i]->recipientOfflineCount==RECIPIENT_OFFLINE_MAX_COUNT)
|
||||
{
|
||||
rakFree(connectionRequestList[i]->passwordData);
|
||||
delete connectionRequestList[i];
|
||||
connectionRequestList.RemoveAtIndex(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
connectionRequestList[i]->nextActionTime=time+RECIPIENT_OFFLINE_MESSAGE_INTERVAL;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
PluginReceiveResult NatPunchthrough::OnReceive(RakPeerInterface *peer, Packet *packet)
|
||||
{
|
||||
unsigned char packetId;
|
||||
if (packet->length>sizeof(MessageID)+sizeof(RakNetTime) && packet->data[0]==ID_TIMESTAMP)
|
||||
packetId=packet->data[sizeof(MessageID)+sizeof(RakNetTime)];
|
||||
else
|
||||
packetId=packet->data[0];
|
||||
|
||||
switch (packetId)
|
||||
{
|
||||
case ID_OUT_OF_BAND_INTERNAL:
|
||||
if (packet->length>=2 && packet->data[1]==ID_NAT_ADVERTISE_RECIPIENT_PORT)
|
||||
{
|
||||
OnNATAdvertiseRecipientPort(peer, packet);
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE; // Absorb
|
||||
}
|
||||
break;
|
||||
case ID_NAT_PUNCHTHROUGH_REQUEST:
|
||||
OnPunchthroughRequest(peer, packet);
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE; // Absorb
|
||||
case ID_NAT_TARGET_CONNECTION_LOST:
|
||||
RemoveRequestByFacilitator(packet->systemAddress);
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
||||
case ID_NAT_TARGET_NOT_CONNECTED:
|
||||
RemoveRequestByFacilitator(packet->systemAddress);
|
||||
return RR_CONTINUE_PROCESSING;
|
||||
case ID_CONNECTION_ATTEMPT_FAILED:
|
||||
return OnConnectionAttemptFailed(packet);
|
||||
case ID_CONNECTION_REQUEST_ACCEPTED:
|
||||
return OnConnectionRequestAccepted(packet);
|
||||
case ID_NEW_INCOMING_CONNECTION:
|
||||
return OnNewIncomingConnection(packet);
|
||||
case ID_NAT_CONNECT_AT_TIME:
|
||||
OnConnectAtTime(peer, packet);
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE; // Absorb
|
||||
case ID_NAT_SEND_OFFLINE_MESSAGE_AT_TIME:
|
||||
OnSendOfflineMessageAtTime(peer, packet);
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE; // Absorb
|
||||
case ID_DISCONNECTION_NOTIFICATION:
|
||||
case ID_CONNECTION_LOST:
|
||||
OnCloseConnection(peer, packet->systemAddress);
|
||||
break;
|
||||
}
|
||||
|
||||
return RR_CONTINUE_PROCESSING;
|
||||
}
|
||||
void NatPunchthrough::OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress)
|
||||
{
|
||||
(void) peer;
|
||||
if (allowFacilitation==false)
|
||||
return;
|
||||
|
||||
// If in ping mode, send ID_NAT_TARGET_CONNECTION_LOST
|
||||
if (connectionRequestList.Size())
|
||||
{
|
||||
unsigned i;
|
||||
i=0;
|
||||
while (i < connectionRequestList.Size())
|
||||
{
|
||||
if (connectionRequestList[i]->facilitatingConnection &&
|
||||
(connectionRequestList[i]->receiverPublic==systemAddress ||
|
||||
connectionRequestList[i]->senderPublic==systemAddress))
|
||||
{
|
||||
LogOut(FormatString("Facilitator: Lost connection to %s\n", systemAddress.ToString(true)));
|
||||
|
||||
// This field is not used by the facilitator.
|
||||
RakAssert(connectionRequestList[i]->passwordData==0);
|
||||
|
||||
if (connectionRequestList[i]->senderPublic==systemAddress)
|
||||
{
|
||||
RakNet::BitStream outBitstream;
|
||||
outBitstream.Write((MessageID)ID_NAT_TARGET_CONNECTION_LOST);
|
||||
outBitstream.Write(connectionRequestList[i]->receiverPublic);
|
||||
rakPeer->Send(&outBitstream, HIGH_PRIORITY, RELIABLE, 0, connectionRequestList[i]->senderPublic, false);
|
||||
}
|
||||
|
||||
rakFree(connectionRequestList[i]->passwordData);
|
||||
delete connectionRequestList[i];
|
||||
connectionRequestList.RemoveAtIndex(i);
|
||||
}
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
void NatPunchthrough::OnShutdown(RakPeerInterface *peer)
|
||||
{
|
||||
(void) peer;
|
||||
}
|
||||
void NatPunchthrough::RemoveRequestByFacilitator(SystemAddress systemAddress)
|
||||
{
|
||||
unsigned i;
|
||||
i=0;
|
||||
while (i < connectionRequestList.Size())
|
||||
{
|
||||
if (connectionRequestList[i]->facilitator==systemAddress)
|
||||
{
|
||||
rakFree(connectionRequestList[i]->passwordData);
|
||||
delete connectionRequestList[i];
|
||||
connectionRequestList.RemoveAtIndex(i);
|
||||
break;
|
||||
}
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
PluginReceiveResult NatPunchthrough::OnConnectionAttemptFailed(Packet *packet)
|
||||
{
|
||||
// SystemAddress publicOne, publicTwo;
|
||||
// SystemAddress privateZero, privateOne, privateTwo;
|
||||
// SystemAddress receiverAddrWithFPort;
|
||||
// SystemAddress receiverAddrWithSPort;
|
||||
// SystemAddress receiverAddrWithIPort;
|
||||
|
||||
|
||||
DataStructures::List<SystemAddress> fallbackAddresses;
|
||||
|
||||
unsigned i,addressIndex;
|
||||
i=0;
|
||||
while (i < connectionRequestList.Size())
|
||||
{
|
||||
// Advertised port has priority. If set, use that. If fails on that, fail totally.
|
||||
if (connectionRequestList[i]->advertisedPort!=0)
|
||||
{
|
||||
SystemAddress advertisedAddress;
|
||||
advertisedAddress.binaryAddress=connectionRequestList[i]->receiverPublic.binaryAddress;
|
||||
advertisedAddress.port=connectionRequestList[i]->advertisedPort;
|
||||
if (packet->systemAddress==advertisedAddress)
|
||||
{
|
||||
// Couldn't connect to the advertised port, meaning uni-directional communication. Total failure.
|
||||
// Shouldn't really ever hit this.
|
||||
LogOut(FormatString("Failed to connect to remotely transmitted port from %s.\nCommunication would only be unidirectional.\n", packet->systemAddress.ToString(true)));
|
||||
|
||||
rakFree(connectionRequestList[i]->passwordData);
|
||||
delete connectionRequestList[i];
|
||||
packet->systemAddress=connectionRequestList[i]->receiverPublic;
|
||||
connectionRequestList.RemoveAtIndex(i);
|
||||
return RR_CONTINUE_PROCESSING;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogOut(FormatString("Connecting to remotely advertised address %s\n", advertisedAddress.ToString(true)));
|
||||
rakPeer->Connect(advertisedAddress.ToString(false), advertisedAddress.port, connectionRequestList[i]->passwordData, connectionRequestList[i]->passwordDataLength);
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
||||
}
|
||||
}
|
||||
|
||||
connectionRequestList[i]->GetAddressList(rakPeer, fallbackAddresses, connectionRequestList[i]->receiverPublic, connectionRequestList[i]->receiverPrivate, false);
|
||||
addressIndex=fallbackAddresses.GetIndexOf(packet->systemAddress);
|
||||
|
||||
if (addressIndex != (unsigned) -1)
|
||||
{
|
||||
if (addressIndex<fallbackAddresses.Size()-1)
|
||||
{
|
||||
LogOut(FormatString("Connecting to fallback address %s\n", fallbackAddresses[addressIndex+1].ToString(true)));
|
||||
rakPeer->Connect(fallbackAddresses[addressIndex+1].ToString(false), fallbackAddresses[addressIndex+1].port, connectionRequestList[i]->passwordData, connectionRequestList[i]->passwordDataLength);
|
||||
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogOut(FormatString("NAT punchthrough failed."));
|
||||
LogOut(FormatString("Last attempted address=%s\n", packet->systemAddress.ToString()));
|
||||
LogOut(FormatString("Address list:\n"));
|
||||
unsigned j;
|
||||
for (j=0; j < fallbackAddresses.Size(); j++)
|
||||
LogOut(FormatString("%i. %s\n", j+1, fallbackAddresses[j].ToString()));
|
||||
|
||||
// Totally failed
|
||||
rakFree(connectionRequestList[i]->passwordData);
|
||||
delete connectionRequestList[i];
|
||||
|
||||
// Both attempts failed, return the notification
|
||||
packet->systemAddress=connectionRequestList[i]->receiverPublic;
|
||||
connectionRequestList.RemoveAtIndex(i);
|
||||
return RR_CONTINUE_PROCESSING;
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// Unrelated, return notification
|
||||
return RR_CONTINUE_PROCESSING;
|
||||
}
|
||||
PluginReceiveResult NatPunchthrough::OnConnectionRequestAccepted(Packet *packet)
|
||||
{
|
||||
unsigned i;
|
||||
i=0;
|
||||
while (i < connectionRequestList.Size())
|
||||
{
|
||||
DataStructures::List<SystemAddress> fallbackAddresses;
|
||||
connectionRequestList[i]->GetAddressList(rakPeer, fallbackAddresses, connectionRequestList[i]->receiverPublic, connectionRequestList[i]->receiverPrivate, false);
|
||||
|
||||
if (fallbackAddresses.GetIndexOf(packet->systemAddress)!=(unsigned) -1)
|
||||
{
|
||||
rakFree(connectionRequestList[i]->passwordData);
|
||||
delete connectionRequestList[i];
|
||||
connectionRequestList.RemoveAtIndex(i);
|
||||
return RR_CONTINUE_PROCESSING;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// return to user
|
||||
return RR_CONTINUE_PROCESSING;
|
||||
}
|
||||
PluginReceiveResult NatPunchthrough::OnNewIncomingConnection(Packet *packet)
|
||||
{
|
||||
unsigned i;
|
||||
i=0;
|
||||
while (i < connectionRequestList.Size())
|
||||
{
|
||||
DataStructures::List<SystemAddress> fallbackAddresses;
|
||||
connectionRequestList[i]->GetAddressList(rakPeer, fallbackAddresses, connectionRequestList[i]->senderPublic, connectionRequestList[i]->senderPrivate, false);
|
||||
if (fallbackAddresses.GetIndexOf(packet->systemAddress)!=(unsigned) -1)
|
||||
{
|
||||
rakFree(connectionRequestList[i]->passwordData);
|
||||
delete connectionRequestList[i];
|
||||
connectionRequestList.RemoveAtIndex(i);
|
||||
return RR_CONTINUE_PROCESSING;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// return to user
|
||||
return RR_CONTINUE_PROCESSING;
|
||||
}
|
||||
void NatPunchthrough::OnPunchthroughRequest(RakPeerInterface *peer, Packet *packet)
|
||||
{
|
||||
(void) peer;
|
||||
|
||||
if (allowFacilitation==false)
|
||||
return;
|
||||
|
||||
SystemAddress targetPublic;
|
||||
RakNet::BitStream inBitstream(packet->data, packet->length, false);
|
||||
RakNet::BitStream outBitstream;
|
||||
inBitstream.IgnoreBits(8);
|
||||
inBitstream.IgnoreBits(1);
|
||||
if (inBitstream.Read(targetPublic)==false)
|
||||
return;
|
||||
if (rakPeer->IsConnected(targetPublic)==false)
|
||||
{
|
||||
LogOut("ID_NAT_TARGET_NOT_CONNECTED\n");
|
||||
LogOut(FormatString("targetPublic=%s", targetPublic.ToString(true)));
|
||||
LogOut("Connection list:\n");
|
||||
SystemAddress remoteSystems[16];
|
||||
unsigned short numberOfSystems=16;
|
||||
rakPeer->GetConnectionList(remoteSystems, &numberOfSystems);
|
||||
for (unsigned i=0; i < numberOfSystems; i++)
|
||||
LogOut(FormatString("%i. %s\n", i+1, remoteSystems[i].ToString(true)));
|
||||
|
||||
outBitstream.Write((MessageID)ID_NAT_TARGET_NOT_CONNECTED);
|
||||
outBitstream.Write(targetPublic);
|
||||
rakPeer->Send(&outBitstream, HIGH_PRIORITY, RELIABLE, 0, packet->systemAddress, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned i;
|
||||
for (i=0; i<connectionRequestList.Size();i++)
|
||||
{
|
||||
if (connectionRequestList[i]->receiverPublic==targetPublic &&
|
||||
connectionRequestList[i]->senderPublic==packet->systemAddress)
|
||||
{
|
||||
outBitstream.Write((MessageID)ID_NAT_IN_PROGRESS);
|
||||
outBitstream.Write(targetPublic);
|
||||
rakPeer->Send(&outBitstream, HIGH_PRIORITY, RELIABLE, 0, packet->systemAddress, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Remember this connection request
|
||||
NatPunchthrough::ConnectionRequest *connectionRequest = new NatPunchthrough::ConnectionRequest;
|
||||
connectionRequest->receiverPublic=targetPublic;
|
||||
connectionRequest->facilitator=UNASSIGNED_SYSTEM_ADDRESS;
|
||||
connectionRequest->passwordData = 0;
|
||||
connectionRequest->senderPublic=packet->systemAddress;
|
||||
connectionRequest->advertisedPort=0;
|
||||
connectionRequest->passwordDataLength=0;
|
||||
connectionRequest->attemptedConnection=false; // Not actually necessary, only used when facilitatingConnection==false
|
||||
connectionRequest->facilitatingConnection=true;
|
||||
connectionRequest->nextActionTime=RakNet::GetTime()+PING_INTERVAL;
|
||||
connectionRequest->pingCount=1;
|
||||
connectionRequest->timeoutTime=RakNet::GetTime()+30000;
|
||||
connectionRequestList.Insert(connectionRequest);
|
||||
|
||||
rakPeer->Ping(connectionRequest->receiverPublic);
|
||||
rakPeer->Ping(connectionRequest->senderPublic);
|
||||
}
|
||||
}
|
||||
void NatPunchthrough::OnNATAdvertiseRecipientPort(RakPeerInterface *peer, Packet *packet)
|
||||
{
|
||||
(void) peer;
|
||||
|
||||
// Find this connection request and set the connection timer.
|
||||
RakNet::BitStream inBitstream(packet->data, packet->length, false);
|
||||
inBitstream.IgnoreBits(16);
|
||||
SystemAddress recipientPublic;
|
||||
inBitstream.Read(recipientPublic);
|
||||
|
||||
unsigned i;
|
||||
for (i=0; i < connectionRequestList.Size(); i++)
|
||||
{
|
||||
if (connectionRequestList[i]->receiverPublic==recipientPublic)
|
||||
{
|
||||
// Only write once, otherwise if it was different it would mess up OnConnectionAttemptFailed which only gives one try at a specific advertised port.
|
||||
if (connectionRequestList[i]->advertisedPort==0)
|
||||
{
|
||||
LogOut(FormatString("Got datagram from receiver with address %s\n", packet->systemAddress.ToString(true)));
|
||||
connectionRequestList[i]->advertisedPort=packet->systemAddress.port;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void NatPunchthrough::OnConnectAtTime(RakPeerInterface *peer, Packet *packet)
|
||||
{
|
||||
(void) peer;
|
||||
|
||||
// Find this connection request and set the connection timer.
|
||||
unsigned i;
|
||||
for (i=0; i < connectionRequestList.Size(); i++)
|
||||
{
|
||||
if (connectionRequestList[i]->nextActionTime==0 &&
|
||||
connectionRequestList[i]->facilitatingConnection==false &&
|
||||
connectionRequestList[i]->facilitator==packet->systemAddress)
|
||||
{
|
||||
// Send a message at the router to the remote system, to open up the firewall / NAT
|
||||
// LogOut(FormatString("Sender sending TTL %i hops to %s\n", TTL_HOPS, connectionRequestList[i]->receiverPublic.ToString(false)));
|
||||
//BroadcastTTL(peer, connectionRequestList[i]->receiverPublic.ToString(false));
|
||||
// peer->SendTTL(connectionRequestList[i]->receiverPublic.ToString(false), connectionRequestList[i]->receiverPublic.port, TTL_HOPS, 0);
|
||||
//peer->SendTTL(connectionRequestList[i]->receiverPublic.ToString(false), connectionRequestList[i]->receiverPublic.port+1, TTL_HOPS, 0);
|
||||
|
||||
|
||||
|
||||
RakNet::BitStream inBitstream(packet->data, packet->length, false);
|
||||
inBitstream.IgnoreBits(8); // ID_TIMESTAMP
|
||||
inBitstream.Read(connectionRequestList[i]->nextActionTime);
|
||||
inBitstream.IgnoreBits(8); // ID_NAT_CONNECT_AT_TIME
|
||||
inBitstream.IgnoreBits(1);
|
||||
inBitstream.Read(connectionRequestList[i]->receiverPrivate);
|
||||
|
||||
// peer->SendTTL(connectionRequestList[i]->receiverPrivate.ToString(false), connectionRequestList[i]->receiverPrivate.port, TTL_HOPS, 0);
|
||||
|
||||
// Open all possible sender ports, so if they send to us on said port when guessing via ID_NAT_ADVERTISE_RECIPIENT_PORT, we can get the incoming packet
|
||||
DataStructures::List<SystemAddress> fallbackAddresses;
|
||||
ConnectionRequest *connectionRequest = connectionRequestList[i];
|
||||
connectionRequest->GetAddressList(peer, fallbackAddresses, connectionRequest->receiverPublic, connectionRequest->receiverPrivate, true);
|
||||
unsigned fallbackIndex;
|
||||
for (fallbackIndex=0; fallbackIndex < fallbackAddresses.Size(); fallbackIndex++)
|
||||
rakPeer->SendTTL(fallbackAddresses[fallbackIndex].ToString(false), fallbackAddresses[fallbackIndex].port, TTL_HOPS);
|
||||
LogOut(FormatString("Sender sending TTL with %i hops on %i ports to %s\n", TTL_HOPS, fallbackAddresses.Size(), connectionRequest->receiverPublic.ToString(false)));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
void NatPunchthrough::OnSendOfflineMessageAtTime(RakPeerInterface *peer, Packet *packet)
|
||||
{
|
||||
(void) peer;
|
||||
|
||||
RakNet::BitStream inBitstream(packet->data, packet->length, false);
|
||||
RakNetTime nextActionTime;
|
||||
inBitstream.IgnoreBits(8); // ID_TIMESTAMP
|
||||
SystemAddress senderPublic, senderPrivate;
|
||||
inBitstream.Read(nextActionTime); // TIMESTAMP
|
||||
inBitstream.IgnoreBits(8); // ID_NAT_SEND_OFFLINE_MESSAGE_AT_TIME
|
||||
inBitstream.IgnoreBits(1); // 1 bit offset
|
||||
inBitstream.Read(senderPublic);
|
||||
if (inBitstream.Read(senderPrivate)==false)
|
||||
return;
|
||||
|
||||
// Just use the TTL2 here
|
||||
NatPunchthrough::ConnectionRequest *connectionRequest = new NatPunchthrough::ConnectionRequest;
|
||||
connectionRequest->receiverPublic=UNASSIGNED_SYSTEM_ADDRESS;
|
||||
connectionRequest->receiverPrivate=UNASSIGNED_SYSTEM_ADDRESS;
|
||||
connectionRequest->facilitator=packet->systemAddress;
|
||||
connectionRequest->senderPublic=senderPublic;
|
||||
connectionRequest->senderPrivate=senderPrivate;
|
||||
connectionRequest->passwordData = 0;
|
||||
connectionRequest->passwordDataLength=0;
|
||||
connectionRequest->advertisedPort=0;
|
||||
connectionRequest->attemptedConnection=false;
|
||||
connectionRequest->facilitatingConnection=false;
|
||||
connectionRequest->nextActionTime=nextActionTime;
|
||||
connectionRequest->pingCount=0;
|
||||
connectionRequest->timeoutTime=RakNet::GetTime()+30000;
|
||||
connectionRequest->recipientOfflineCount=0;
|
||||
connectionRequestList.Insert(connectionRequest);
|
||||
|
||||
// Send a message at the router to the remote system, to open up the firewall / NAT
|
||||
// LogOut(FormatString("Recipient sending TTL %i hops to %s\n", TTL_HOPS, senderPublic.ToString(false)));
|
||||
//BroadcastTTL(peer, connectionRequest->senderPublic.ToString(false));
|
||||
// peer->SendTTL(senderPublic.ToString(false),senderPublic.port, TTL_HOPS, 0);
|
||||
// peer->SendTTL(senderPublic.ToString(false),senderPublic.port+1, TTL_HOPS, 0);
|
||||
// peer->SendTTL(senderPrivate.ToString(false),senderPrivate.port, TTL_HOPS, 0);
|
||||
|
||||
|
||||
// Open all possible sender ports, so if they send to us on said port, the router doesn't ban them
|
||||
DataStructures::List<SystemAddress> fallbackAddresses;
|
||||
connectionRequest->GetAddressList(peer, fallbackAddresses, connectionRequest->senderPublic, connectionRequest->senderPrivate, true);
|
||||
unsigned fallbackIndex;
|
||||
for (fallbackIndex=0; fallbackIndex < fallbackAddresses.Size(); fallbackIndex++)
|
||||
rakPeer->SendTTL(fallbackAddresses[fallbackIndex].ToString(false), fallbackAddresses[fallbackIndex].port, TTL_HOPS);
|
||||
LogOut(FormatString("Recipient sending TTL with %i hops on %i ports to %s\n", TTL_HOPS, fallbackAddresses.Size(), senderPublic.ToString()));
|
||||
|
||||
|
||||
/*
|
||||
SystemAddress senderAddrWithFPort;
|
||||
SystemAddress senderAddrWithRPort;
|
||||
SystemAddress senderAddrWithIPort;
|
||||
senderAddrWithFPort=connectionRequest->senderPublic;
|
||||
senderAddrWithFPort.port=connectionRequest->facilitator.port;
|
||||
senderAddrWithRPort=connectionRequest->senderPublic;
|
||||
senderAddrWithRPort.port=rakPeer->GetExternalID(connectionRequest->facilitator).port;
|
||||
senderAddrWithIPort=connectionRequest->senderPublic;
|
||||
senderAddrWithIPort.port=rakPeer->GetInternalID().port;
|
||||
*/
|
||||
// Send offline message to this system, hopefully at the exact same time that system tries to connect to us.
|
||||
/*
|
||||
rakPeer->SendTTL(senderAddrWithFPort.ToString(false), senderAddrWithFPort.port, false);
|
||||
rakPeer->SendTTL(senderAddrWithRPort.ToString(false), senderAddrWithRPort.port, false);
|
||||
rakPeer->SendTTL(senderAddrWithIPort.ToString(false), senderAddrWithIPort.port, false);
|
||||
*/
|
||||
}
|
||||
void NatPunchthrough::LogOut(const char *l)
|
||||
{
|
||||
if (log)
|
||||
log->OnMessage(l);
|
||||
}
|
||||
void NatPunchthrough::SetLogger(NatPunchthroughLogger *l)
|
||||
{
|
||||
log=l;
|
||||
}
|
||||
void NatPunchthrough::ConnectionRequest::GetAddressList(RakPeerInterface *rakPeer, DataStructures::List<SystemAddress> &fallbackAddresses, SystemAddress publicAddress, SystemAddress privateAddress, bool excludeConnected)
|
||||
{
|
||||
SystemAddress fallback;
|
||||
fallbackAddresses.Clear(true);
|
||||
fallback.binaryAddress=publicAddress.binaryAddress;
|
||||
|
||||
fallback.port=publicAddress.port;
|
||||
if (excludeConnected==false || rakPeer->IsConnected(fallback,true)==false)
|
||||
fallbackAddresses.Insert(fallback);
|
||||
|
||||
if (privateAddress!=publicAddress && privateAddress!=UNASSIGNED_SYSTEM_ADDRESS && (excludeConnected==false ||rakPeer->IsConnected(privateAddress,true)==false))
|
||||
fallbackAddresses.Insert(privateAddress);
|
||||
|
||||
fallback.port=publicAddress.port+1;
|
||||
if (excludeConnected==false || rakPeer->IsConnected(fallback,true)==false)
|
||||
fallbackAddresses.Insert(fallback);
|
||||
fallback.port=publicAddress.port+2;
|
||||
if (excludeConnected==false || rakPeer->IsConnected(fallback,true)==false)
|
||||
fallbackAddresses.Insert(fallback);
|
||||
|
||||
fallback.port=rakPeer->GetInternalID().port;
|
||||
if (fallbackAddresses.GetIndexOf(fallback)==(unsigned) -1 && (excludeConnected==false || rakPeer->IsConnected(fallback,true)==false))
|
||||
fallbackAddresses.Insert(fallback);
|
||||
fallback.port=rakPeer->GetInternalID().port+1;
|
||||
if (fallbackAddresses.GetIndexOf(fallback)==(unsigned) -1 && (excludeConnected==false || rakPeer->IsConnected(fallback,true)==false))
|
||||
fallbackAddresses.Insert(fallback);
|
||||
fallback.port=rakPeer->GetInternalID().port+2;
|
||||
if (fallbackAddresses.GetIndexOf(fallback)==(unsigned) -1 && (excludeConnected==false || rakPeer->IsConnected(fallback,true)==false))
|
||||
fallbackAddresses.Insert(fallback);
|
||||
|
||||
// Try to keep down the number of fallbacks or the router may run out of mappings
|
||||
/*
|
||||
fallback.port=publicAddress.port+3;
|
||||
if (excludeConnected==false || rakPeer->IsConnected(fallback,true)==false)
|
||||
fallbackAddresses.Insert(fallback);
|
||||
fallback.port=publicAddress.port+4;
|
||||
if (excludeConnected==false || rakPeer->IsConnected(fallback,true)==false)
|
||||
fallbackAddresses.Insert(fallback);
|
||||
|
||||
fallback.port=rakPeer->GetInternalID().port+3;
|
||||
if (fallbackAddresses.GetIndexOf(fallback)==(unsigned) -1 && (excludeConnected==false || rakPeer->IsConnected(fallback,true)==false))
|
||||
fallbackAddresses.Insert(fallback);
|
||||
fallback.port=rakPeer->GetInternalID().port+4;
|
||||
if (fallbackAddresses.GetIndexOf(fallback)==(unsigned) -1 && (excludeConnected==false || rakPeer->IsConnected(fallback,true)==false))
|
||||
fallbackAddresses.Insert(fallback);
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( pop )
|
||||
#endif
|
Reference in New Issue
Block a user