Files
mcpe/thirdparty/raknet/NatPunchthroughServer.cpp
iProgramInCpp 9642818a88 * Initial commit.
:)
2023-07-30 22:22:02 +03:00

625 lines
20 KiB
C++

/*
* Copyright (c) 2014, Oculus VR, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "NativeFeatureIncludes.h"
#if _RAKNET_SUPPORT_NatPunchthroughServer==1
#include "NatPunchthroughServer.h"
#include "SocketLayer.h"
#include "BitStream.h"
#include "MessageIdentifiers.h"
#include "RakPeerInterface.h"
#include "MTUSize.h"
#include "GetTime.h"
#include "PacketLogger.h"
using namespace RakNet;
void NatPunchthroughServerDebugInterface_Printf::OnServerMessage(const char *msg)
{
printf("%s\n", msg);
}
#if _RAKNET_SUPPORT_PacketLogger==1
void NatPunchthroughServerDebugInterface_PacketLogger::OnServerMessage(const char *msg)
{
if (pl)
{
pl->WriteMiscellaneous("Nat", msg);
}
}
#endif
void NatPunchthroughServer::User::DeleteConnectionAttempt(NatPunchthroughServer::ConnectionAttempt *ca)
{
unsigned int index = connectionAttempts.GetIndexOf(ca);
if ((unsigned int)index!=(unsigned int)-1)
{
RakNet::OP_DELETE(ca,_FILE_AND_LINE_);
connectionAttempts.RemoveAtIndex(index);
}
}
void NatPunchthroughServer::User::DerefConnectionAttempt(NatPunchthroughServer::ConnectionAttempt *ca)
{
unsigned int index = connectionAttempts.GetIndexOf(ca);
if ((unsigned int)index!=(unsigned int)-1)
{
connectionAttempts.RemoveAtIndex(index);
}
}
bool NatPunchthroughServer::User::HasConnectionAttemptToUser(User *user)
{
unsigned int index;
for (index=0; index < connectionAttempts.Size(); index++)
{
if (connectionAttempts[index]->recipient->guid==user->guid ||
connectionAttempts[index]->sender->guid==user->guid)
return true;
}
return false;
}
void NatPunchthroughServer::User::LogConnectionAttempts(RakNet::RakString &rs)
{
rs.Clear();
unsigned int index;
char guidStr[128], ipStr[128];
guid.ToString(guidStr);
systemAddress.ToString(true,ipStr);
rs=RakNet::RakString("User systemAddress=%s guid=%s\n", ipStr, guidStr);
rs+=RakNet::RakString("%i attempts in list:\n", connectionAttempts.Size());
for (index=0; index < connectionAttempts.Size(); index++)
{
rs+=RakNet::RakString("%i. SessionID=%i ", index+1, connectionAttempts[index]->sessionId);
if (connectionAttempts[index]->sender==this)
rs+="(We are sender) ";
else
rs+="(We are recipient) ";
if (isReady)
rs+="(READY TO START) ";
else
rs+="(NOT READY TO START) ";
if (connectionAttempts[index]->attemptPhase==NatPunchthroughServer::ConnectionAttempt::NAT_ATTEMPT_PHASE_NOT_STARTED)
rs+="(NOT_STARTED). ";
else
rs+="(GETTING_RECENT_PORTS). ";
if (connectionAttempts[index]->sender==this)
{
connectionAttempts[index]->recipient->guid.ToString(guidStr);
connectionAttempts[index]->recipient->systemAddress.ToString(true,ipStr);
}
else
{
connectionAttempts[index]->sender->guid.ToString(guidStr);
connectionAttempts[index]->sender->systemAddress.ToString(true,ipStr);
}
rs+=RakNet::RakString("Target systemAddress=%s, guid=%s.\n", ipStr, guidStr);
}
}
int RakNet::NatPunchthroughServer::NatPunchthroughUserComp( const RakNetGUID &key, User * const &data )
{
if (key < data->guid)
return -1;
if (key > data->guid)
return 1;
return 0;
}
STATIC_FACTORY_DEFINITIONS(NatPunchthroughServer,NatPunchthroughServer);
NatPunchthroughServer::NatPunchthroughServer()
{
lastUpdate=0;
sessionId=0;
natPunchthroughServerDebugInterface=0;
for (int i=0; i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++)
boundAddresses[i]=UNASSIGNED_SYSTEM_ADDRESS;
boundAddressCount=0;
}
NatPunchthroughServer::~NatPunchthroughServer()
{
User *user, *otherUser;
ConnectionAttempt *connectionAttempt;
unsigned int j;
while(users.Size())
{
user = users[0];
for (j=0; j < user->connectionAttempts.Size(); j++)
{
connectionAttempt=user->connectionAttempts[j];
if (connectionAttempt->sender==user)
otherUser=connectionAttempt->recipient;
else
otherUser=connectionAttempt->sender;
otherUser->DeleteConnectionAttempt(connectionAttempt);
}
RakNet::OP_DELETE(user,_FILE_AND_LINE_);
users[0]=users[users.Size()-1];
users.RemoveAtIndex(users.Size()-1);
}
}
void NatPunchthroughServer::SetDebugInterface(NatPunchthroughServerDebugInterface *i)
{
natPunchthroughServerDebugInterface=i;
}
void NatPunchthroughServer::Update(void)
{
ConnectionAttempt *connectionAttempt;
User *user, *recipient;
unsigned int i,j;
RakNet::Time time = RakNet::GetTime();
if (time > lastUpdate+250)
{
lastUpdate=time;
for (i=0; i < users.Size(); i++)
{
user=users[i];
for (j=0; j < user->connectionAttempts.Size(); j++)
{
connectionAttempt=user->connectionAttempts[j];
if (connectionAttempt->sender==user)
{
if (connectionAttempt->attemptPhase!=ConnectionAttempt::NAT_ATTEMPT_PHASE_NOT_STARTED &&
time > connectionAttempt->startTime &&
time > 10000 + connectionAttempt->startTime ) // Formerly 5000, but sometimes false positives
{
RakNet::BitStream outgoingBs;
// that other system might not be running the plugin
outgoingBs.Write((MessageID)ID_NAT_TARGET_UNRESPONSIVE);
outgoingBs.Write(connectionAttempt->recipient->guid);
outgoingBs.Write(connectionAttempt->sessionId);
rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,connectionAttempt->sender->systemAddress,false);
// 05/28/09 Previously only told sender about ID_NAT_CONNECTION_TO_TARGET_LOST
// However, recipient may be expecting it due to external code
// In that case, recipient would never get any response if the sender dropped
outgoingBs.Reset();
outgoingBs.Write((MessageID)ID_NAT_TARGET_UNRESPONSIVE);
outgoingBs.Write(connectionAttempt->sender->guid);
outgoingBs.Write(connectionAttempt->sessionId);
rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,connectionAttempt->recipient->systemAddress,false);
connectionAttempt->sender->isReady=true;
connectionAttempt->recipient->isReady=true;
recipient=connectionAttempt->recipient;
if (natPunchthroughServerDebugInterface)
{
char str[1024];
char addr1[128], addr2[128];
// 8/01/09 Fixed bug where this was after DeleteConnectionAttempt()
connectionAttempt->sender->systemAddress.ToString(true,addr1);
connectionAttempt->recipient->systemAddress.ToString(true,addr2);
sprintf(str, "Sending ID_NAT_TARGET_UNRESPONSIVE to sender %s and recipient %s.", addr1, addr2);
natPunchthroughServerDebugInterface->OnServerMessage(str);
RakNet::RakString log;
connectionAttempt->sender->LogConnectionAttempts(log);
connectionAttempt->recipient->LogConnectionAttempts(log);
}
connectionAttempt->sender->DerefConnectionAttempt(connectionAttempt);
connectionAttempt->recipient->DeleteConnectionAttempt(connectionAttempt);
StartPunchthroughForUser(user);
StartPunchthroughForUser(recipient);
break;
}
}
}
}
}
}
PluginReceiveResult NatPunchthroughServer::OnReceive(Packet *packet)
{
switch (packet->data[0])
{
case ID_NAT_PUNCHTHROUGH_REQUEST:
OnNATPunchthroughRequest(packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
case ID_NAT_GET_MOST_RECENT_PORT:
OnGetMostRecentPort(packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
case ID_NAT_CLIENT_READY:
OnClientReady(packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
case ID_NAT_REQUEST_BOUND_ADDRESSES:
{
RakNet::BitStream outgoingBs;
outgoingBs.Write((MessageID)ID_NAT_RESPOND_BOUND_ADDRESSES);
if (boundAddresses[0]==UNASSIGNED_SYSTEM_ADDRESS)
{
DataStructures::List<RakNetSocket2* > sockets;
rakPeerInterface->GetSockets(sockets);
for (int i=0; i < sockets.Size() && i < MAXIMUM_NUMBER_OF_INTERNAL_IDS; i++)
{
boundAddresses[i]=sockets[i]->GetBoundAddress();
boundAddressCount++;
}
}
outgoingBs.Write(boundAddressCount);
for (int i=0; i < boundAddressCount; i++)
{
outgoingBs.Write(boundAddresses[i]);
}
rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false);
}
return RR_STOP_PROCESSING_AND_DEALLOCATE;
case ID_NAT_PING:
{
}
return RR_STOP_PROCESSING_AND_DEALLOCATE;
case ID_OUT_OF_BAND_INTERNAL:
if (packet->length>=2 && packet->data[1]==ID_NAT_PING)
{
RakNet::BitStream bs(packet->data,packet->length,false);
bs.IgnoreBytes(sizeof(MessageID)*2);
uint16_t externalPort;
bs.Read(externalPort);
RakNet::BitStream outgoingBs;
outgoingBs.Write((MessageID)ID_NAT_PONG);
outgoingBs.Write(externalPort);
uint16_t externalPort2 = packet->systemAddress.GetPort();
outgoingBs.Write(externalPort2);
rakPeerInterface->SendOutOfBand((const char*) packet->systemAddress.ToString(false),packet->systemAddress.GetPort(),(const char*) outgoingBs.GetData(),outgoingBs.GetNumberOfBytesUsed());
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
}
return RR_CONTINUE_PROCESSING;
}
void NatPunchthroughServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
{
(void) lostConnectionReason;
(void) systemAddress;
unsigned int i=0;
bool objectExists;
i = users.GetIndexFromKey(rakNetGUID, &objectExists);
if (objectExists)
{
RakNet::BitStream outgoingBs;
DataStructures::List<User *> freedUpInProgressUsers;
User *user = users[i];
User *otherUser;
unsigned int connectionAttemptIndex;
ConnectionAttempt *connectionAttempt;
for (connectionAttemptIndex=0; connectionAttemptIndex < user->connectionAttempts.Size(); connectionAttemptIndex++)
{
connectionAttempt=user->connectionAttempts[connectionAttemptIndex];
outgoingBs.Reset();
if (connectionAttempt->recipient==user)
{
otherUser=connectionAttempt->sender;
}
else
{
otherUser=connectionAttempt->recipient;
}
// 05/28/09 Previously only told sender about ID_NAT_CONNECTION_TO_TARGET_LOST
// However, recipient may be expecting it due to external code
// In that case, recipient would never get any response if the sender dropped
outgoingBs.Write((MessageID)ID_NAT_CONNECTION_TO_TARGET_LOST);
outgoingBs.Write(rakNetGUID);
outgoingBs.Write(connectionAttempt->sessionId);
rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,otherUser->systemAddress,false);
// 4/22/09 - Bug: was checking inProgress, legacy variable not used elsewhere
if (connectionAttempt->attemptPhase==ConnectionAttempt::NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS)
{
otherUser->isReady=true;
freedUpInProgressUsers.Insert(otherUser, _FILE_AND_LINE_ );
}
otherUser->DeleteConnectionAttempt(connectionAttempt);
}
RakNet::OP_DELETE(users[i], _FILE_AND_LINE_);
users.RemoveAtIndex(i);
for (i=0; i < freedUpInProgressUsers.Size(); i++)
{
StartPunchthroughForUser(freedUpInProgressUsers[i]);
}
}
/*
// Also remove from groupPunchthroughRequests
for (i=0; i < users.Size(); i++)
{
bool objectExists;
unsigned int gprIndex;
gprIndex = users[i]->groupPunchthroughRequests.GetIndexFromKey(rakNetGUID, &objectExists);
if (objectExists)
{
// printf("DEBUG %i\n", __LINE__);
RakNet::BitStream outgoingBs;
outgoingBs.Write((MessageID)ID_NAT_TARGET_NOT_CONNECTED);
outgoingBs.Write(rakNetGUID);
rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,users[i]->systemAddress,false);
users[i]->groupPunchthroughRequests.RemoveAtIndex(gprIndex);
}
}
*/
}
void NatPunchthroughServer::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming)
{
(void) systemAddress;
(void) isIncoming;
User *user = RakNet::OP_NEW<User>(_FILE_AND_LINE_);
user->guid=rakNetGUID;
user->mostRecentPort=0;
user->systemAddress=systemAddress;
user->isReady=true;
users.Insert(rakNetGUID, user, true, _FILE_AND_LINE_);
// printf("Adding to users %s\n", rakNetGUID.ToString());
// printf("DEBUG users[0] guid=%s\n", users[0]->guid.ToString());
}
void NatPunchthroughServer::OnNATPunchthroughRequest(Packet *packet)
{
RakNet::BitStream outgoingBs;
RakNet::BitStream incomingBs(packet->data, packet->length, false);
incomingBs.IgnoreBytes(sizeof(MessageID));
RakNetGUID recipientGuid, senderGuid;
incomingBs.Read(recipientGuid);
senderGuid=packet->guid;
unsigned int i;
bool objectExists;
i = users.GetIndexFromKey(senderGuid, &objectExists);
RakAssert(objectExists);
ConnectionAttempt *ca = RakNet::OP_NEW<ConnectionAttempt>(_FILE_AND_LINE_);
ca->sender=users[i];
ca->sessionId=sessionId++;
i = users.GetIndexFromKey(recipientGuid, &objectExists);
if (objectExists==false || ca->sender == ca->recipient)
{
// printf("DEBUG %i\n", __LINE__);
// printf("DEBUG recipientGuid=%s\n", recipientGuid.ToString());
// printf("DEBUG users[0] guid=%s\n", users[0]->guid.ToString());
outgoingBs.Write((MessageID)ID_NAT_TARGET_NOT_CONNECTED);
outgoingBs.Write(recipientGuid);
rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false);
RakNet::OP_DELETE(ca,_FILE_AND_LINE_);
return;
}
ca->recipient=users[i];
if (ca->recipient->HasConnectionAttemptToUser(ca->sender))
{
outgoingBs.Write((MessageID)ID_NAT_ALREADY_IN_PROGRESS);
outgoingBs.Write(recipientGuid);
rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false);
RakNet::OP_DELETE(ca,_FILE_AND_LINE_);
return;
}
ca->sender->connectionAttempts.Insert(ca, _FILE_AND_LINE_ );
ca->recipient->connectionAttempts.Insert(ca, _FILE_AND_LINE_ );
StartPunchthroughForUser(ca->sender);
}
void NatPunchthroughServer::OnClientReady(Packet *packet)
{
unsigned int i;
bool objectExists;
i = users.GetIndexFromKey(packet->guid, &objectExists);
if (objectExists)
{
users[i]->isReady=true;
StartPunchthroughForUser(users[i]);
}
}
void NatPunchthroughServer::OnGetMostRecentPort(Packet *packet)
{
RakNet::BitStream bsIn(packet->data, packet->length, false);
bsIn.IgnoreBytes(sizeof(MessageID));
uint16_t sessionId;
unsigned short mostRecentPort;
bsIn.Read(sessionId);
bsIn.Read(mostRecentPort);
unsigned int i,j;
User *user;
ConnectionAttempt *connectionAttempt;
bool objectExists;
i = users.GetIndexFromKey(packet->guid, &objectExists);
if (natPunchthroughServerDebugInterface)
{
RakNet::RakString log;
char addr1[128], addr2[128];
packet->systemAddress.ToString(true,addr1);
packet->guid.ToString(addr2);
log=RakNet::RakString("Got ID_NAT_GET_MOST_RECENT_PORT from systemAddress %s guid %s. port=%i. sessionId=%i. userFound=%i.", addr1, addr2, mostRecentPort, sessionId, objectExists);
natPunchthroughServerDebugInterface->OnServerMessage(log.C_String());
}
if (objectExists)
{
user=users[i];
user->mostRecentPort=mostRecentPort;
RakNet::Time time = RakNet::GetTime();
for (j=0; j < user->connectionAttempts.Size(); j++)
{
connectionAttempt=user->connectionAttempts[j];
if (connectionAttempt->attemptPhase==ConnectionAttempt::NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS &&
connectionAttempt->sender->mostRecentPort!=0 &&
connectionAttempt->recipient->mostRecentPort!=0 &&
// 04/29/08 add sessionId to prevent processing for other systems
connectionAttempt->sessionId==sessionId)
{
SystemAddress senderSystemAddress = connectionAttempt->sender->systemAddress;
SystemAddress recipientSystemAddress = connectionAttempt->recipient->systemAddress;
SystemAddress recipientTargetAddress = recipientSystemAddress;
SystemAddress senderTargetAddress = senderSystemAddress;
recipientTargetAddress.SetPortHostOrder(connectionAttempt->recipient->mostRecentPort);
senderTargetAddress.SetPortHostOrder(connectionAttempt->sender->mostRecentPort);
// Pick a time far enough in the future that both systems will have gotten the message
int targetPing = rakPeerInterface->GetAveragePing(recipientTargetAddress);
int senderPing = rakPeerInterface->GetAveragePing(senderSystemAddress);
RakNet::Time simultaneousAttemptTime;
if (targetPing==-1 || senderPing==-1)
simultaneousAttemptTime = time + 1500;
else
{
int largerPing = targetPing > senderPing ? targetPing : senderPing;
if (largerPing * 4 < 100)
simultaneousAttemptTime = time + 100;
else
simultaneousAttemptTime = time + (largerPing * 4);
}
if (natPunchthroughServerDebugInterface)
{
RakNet::RakString log;
char addr1[128], addr2[128];
recipientSystemAddress.ToString(true,addr1);
connectionAttempt->recipient->guid.ToString(addr2);
log=RakNet::RakString("Sending ID_NAT_CONNECT_AT_TIME to recipient systemAddress %s guid %s", addr1, addr2);
natPunchthroughServerDebugInterface->OnServerMessage(log.C_String());
}
// Send to recipient timestamped message to connect at time
RakNet::BitStream bsOut;
bsOut.Write((MessageID)ID_TIMESTAMP);
bsOut.Write(simultaneousAttemptTime);
bsOut.Write((MessageID)ID_NAT_CONNECT_AT_TIME);
bsOut.Write(connectionAttempt->sessionId);
bsOut.Write(senderTargetAddress); // Public IP, using most recent port
for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) // Internal IP
bsOut.Write(rakPeerInterface->GetInternalID(senderSystemAddress,j));
bsOut.Write(connectionAttempt->sender->guid);
bsOut.Write(false);
rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,recipientSystemAddress,false);
if (natPunchthroughServerDebugInterface)
{
RakNet::RakString log;
char addr1[128], addr2[128];
senderSystemAddress.ToString(true,addr1);
connectionAttempt->sender->guid.ToString(addr2);
log=RakNet::RakString("Sending ID_NAT_CONNECT_AT_TIME to sender systemAddress %s guid %s", addr1, addr2);
natPunchthroughServerDebugInterface->OnServerMessage(log.C_String());
}
// Same for sender
bsOut.Reset();
bsOut.Write((MessageID)ID_TIMESTAMP);
bsOut.Write(simultaneousAttemptTime);
bsOut.Write((MessageID)ID_NAT_CONNECT_AT_TIME);
bsOut.Write(connectionAttempt->sessionId);
bsOut.Write(recipientTargetAddress); // Public IP, using most recent port
for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++) // Internal IP
bsOut.Write(rakPeerInterface->GetInternalID(recipientSystemAddress,j));
bsOut.Write(connectionAttempt->recipient->guid);
bsOut.Write(true);
rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,senderSystemAddress,false);
connectionAttempt->recipient->DerefConnectionAttempt(connectionAttempt);
connectionAttempt->sender->DeleteConnectionAttempt(connectionAttempt);
// 04/29/08 missing return
return;
}
}
}
else
{
if (natPunchthroughServerDebugInterface)
{
RakNet::RakString log;
char addr1[128], addr2[128];
packet->systemAddress.ToString(true,addr1);
packet->guid.ToString(addr2);
log=RakNet::RakString("Ignoring ID_NAT_GET_MOST_RECENT_PORT from systemAddress %s guid %s", addr1, addr2);
natPunchthroughServerDebugInterface->OnServerMessage(log.C_String());
}
}
}
void NatPunchthroughServer::StartPunchthroughForUser(User *user)
{
if (user->isReady==false)
return;
ConnectionAttempt *connectionAttempt;
User *sender,*recipient,*otherUser;
unsigned int i;
for (i=0; i < user->connectionAttempts.Size(); i++)
{
connectionAttempt=user->connectionAttempts[i];
if (connectionAttempt->sender==user)
{
otherUser=connectionAttempt->recipient;
sender=user;
recipient=otherUser;
}
else
{
otherUser=connectionAttempt->sender;
recipient=user;
sender=otherUser;
}
if (otherUser->isReady)
{
if (natPunchthroughServerDebugInterface)
{
char str[1024];
char addr1[128], addr2[128];
sender->systemAddress.ToString(true,addr1);
recipient->systemAddress.ToString(true,addr2);
sprintf(str, "Sending NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS to sender %s and recipient %s.", addr1, addr2);
natPunchthroughServerDebugInterface->OnServerMessage(str);
}
sender->isReady=false;
recipient->isReady=false;
connectionAttempt->attemptPhase=ConnectionAttempt::NAT_ATTEMPT_PHASE_GETTING_RECENT_PORTS;
connectionAttempt->startTime=RakNet::GetTime();
sender->mostRecentPort=0;
recipient->mostRecentPort=0;
RakNet::BitStream outgoingBs;
outgoingBs.Write((MessageID)ID_NAT_GET_MOST_RECENT_PORT);
// 4/29/09 Write sessionID so we don't use returned port for a system we don't want
outgoingBs.Write(connectionAttempt->sessionId);
rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,sender->systemAddress,false);
rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,recipient->systemAddress,false);
// 4/22/09 - BUG: missing break statement here
break;
}
}
}
#endif // _RAKNET_SUPPORT_*