/*
 *  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_*
