/* * 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_NatTypeDetectionServer==1 #include "NatTypeDetectionServer.h" #include "SocketLayer.h" #include "RakNetSmartPtr.h" #include "SocketIncludes.h" #include "RakPeerInterface.h" #include "MessageIdentifiers.h" #include "GetTime.h" #include "BitStream.h" #include "SocketDefines.h" // #define NTDS_VERBOSE using namespace RakNet; STATIC_FACTORY_DEFINITIONS(NatTypeDetectionServer,NatTypeDetectionServer); NatTypeDetectionServer::NatTypeDetectionServer() { s1p2=s2p3=s3p4=s4p5=0; } NatTypeDetectionServer::~NatTypeDetectionServer() { Shutdown(); } void NatTypeDetectionServer::Startup( const char *nonRakNetIP2, const char *nonRakNetIP3, const char *nonRakNetIP4 #ifdef __native_client__ ,_PP_Instance_ chromeInstance #endif ) { DataStructures::List sockets; rakPeerInterface->GetSockets(sockets); char str[64]; sockets[0]->GetBoundAddress().ToString(false,str); s1p2= CreateNonblockingBoundSocket(str, #ifdef __native_client__ chromeInstance, #endif this); s2p3= CreateNonblockingBoundSocket(nonRakNetIP2, #ifdef __native_client__ chromeInstance, #endif this); s3p4= CreateNonblockingBoundSocket(nonRakNetIP3, #ifdef __native_client__ chromeInstance, #endif this); s4p5= CreateNonblockingBoundSocket(nonRakNetIP4, #ifdef __native_client__ chromeInstance, #endif this); strcpy(s3p4Address, nonRakNetIP3); #if !defined(__native_client__) && !defined(WINDOWS_STORE_RT) if (s3p4->IsBerkleySocket()) ((RNS2_Berkley*) s3p4)->CreateRecvPollingThread(0); #endif } void NatTypeDetectionServer::Shutdown() { if (s1p2!=0) { RakNet::OP_DELETE(s1p2,_FILE_AND_LINE_); s1p2=0; } if (s2p3!=0) { RakNet::OP_DELETE(s2p3,_FILE_AND_LINE_); s2p3=0; } if (s3p4!=0) { #if !defined(__native_client__) && !defined(WINDOWS_STORE_RT) if (s3p4->IsBerkleySocket()) ((RNS2_Berkley *)s3p4)->BlockOnStopRecvPollingThread(); #endif RakNet::OP_DELETE(s3p4,_FILE_AND_LINE_); s3p4=0; } if (s4p5!=0) { RakNet::OP_DELETE(s4p5,_FILE_AND_LINE_); s4p5=0; } bufferedPacketsMutex.Lock(); while (bufferedPackets.Size()) RakNet::OP_DELETE(bufferedPackets.Pop(), _FILE_AND_LINE_); bufferedPacketsMutex.Unlock(); } void NatTypeDetectionServer::Update(void) { int i=0; RakNet::TimeMS time = RakNet::GetTimeMS(); RakNet::BitStream bs; SystemAddress boundAddress; RNS2RecvStruct *recvStruct; bufferedPacketsMutex.Lock(); if (bufferedPackets.Size()>0) recvStruct=bufferedPackets.Pop(); else recvStruct=0; bufferedPacketsMutex.Unlock(); while (recvStruct) { SystemAddress senderAddr = recvStruct->systemAddress; char *data = recvStruct->data; if (data[0]==NAT_TYPE_PORT_RESTRICTED && recvStruct->socket==s3p4) { RakNet::BitStream bsIn((unsigned char*) data,recvStruct->bytesRead,false); RakNetGUID senderGuid; bsIn.IgnoreBytes(sizeof(MessageID)); bool readSuccess = bsIn.Read(senderGuid); RakAssert(readSuccess); if (readSuccess) { unsigned int i = GetDetectionAttemptIndex(senderGuid); if (i!=(unsigned int)-1) { bs.Reset(); bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_RESULT); // If different, then symmetric if (senderAddr!=natDetectionAttempts[i].systemAddress) { #ifdef NTDS_VERBOSE printf("Determined client is symmetric\n"); #endif bs.Write((unsigned char) NAT_TYPE_SYMMETRIC); } else { // else port restricted #ifdef NTDS_VERBOSE printf("Determined client is port restricted\n"); #endif bs.Write((unsigned char) NAT_TYPE_PORT_RESTRICTED); } rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); // Done natDetectionAttempts.RemoveAtIndexFast(i); } else { // RakAssert("i==0 in Update when looking up GUID in NatTypeDetectionServer.cpp. Either a bug or a late resend" && 0); } } else { // RakAssert("Didn't read GUID in Update in NatTypeDetectionServer.cpp. Message format error" && 0); } } DeallocRNS2RecvStruct(recvStruct, _FILE_AND_LINE_); bufferedPacketsMutex.Lock(); if (bufferedPackets.Size()>0) recvStruct=bufferedPackets.Pop(); else recvStruct=0; bufferedPacketsMutex.Unlock(); } /* // Only socket that receives messages is s3p4, to see if the external address is different than that of the connection to rakPeerInterface char data[ MAXIMUM_MTU_SIZE ]; int len; SystemAddress senderAddr; len=NatTypeRecvFrom(data, s3p4, senderAddr); // Client is asking us if this is port restricted. Only client requests of this type come in on s3p4 while (len>0 && data[0]==NAT_TYPE_PORT_RESTRICTED) { RakNet::BitStream bsIn((unsigned char*) data,len,false); RakNetGUID senderGuid; bsIn.IgnoreBytes(sizeof(MessageID)); bool readSuccess = bsIn.Read(senderGuid); RakAssert(readSuccess); if (readSuccess) { unsigned int i = GetDetectionAttemptIndex(senderGuid); if (i!=(unsigned int)-1) { bs.Reset(); bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_RESULT); // If different, then symmetric if (senderAddr!=natDetectionAttempts[i].systemAddress) { #ifdef NTDS_VERBOSE printf("Determined client is symmetric\n"); #endif bs.Write((unsigned char) NAT_TYPE_SYMMETRIC); } else { // else port restricted #ifdef NTDS_VERBOSE printf("Determined client is port restricted\n"); #endif bs.Write((unsigned char) NAT_TYPE_PORT_RESTRICTED); } rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); // Done natDetectionAttempts.RemoveAtIndexFast(i); } else { // RakAssert("i==0 in Update when looking up GUID in NatTypeDetectionServer.cpp. Either a bug or a late resend" && 0); } } else { // RakAssert("Didn't read GUID in Update in NatTypeDetectionServer.cpp. Message format error" && 0); } len=NatTypeRecvFrom(data, s3p4, senderAddr); } */ while (i < (int) natDetectionAttempts.Size()) { if (time > natDetectionAttempts[i].nextStateTime) { RNS2_SendParameters bsp; natDetectionAttempts[i].detectionState=(NATDetectionState)((int)natDetectionAttempts[i].detectionState+1); natDetectionAttempts[i].nextStateTime=time+natDetectionAttempts[i].timeBetweenAttempts; SystemAddress saOut; unsigned char c; bs.Reset(); switch (natDetectionAttempts[i].detectionState) { case STATE_TESTING_NONE_1: case STATE_TESTING_NONE_2: c = NAT_TYPE_NONE; #ifdef NTDS_VERBOSE printf("Testing NAT_TYPE_NONE\n"); #endif // S4P5 sends to C2. If arrived, no NAT. Done. (Else S4P5 potentially banned, do not use again). saOut=natDetectionAttempts[i].systemAddress; saOut.SetPortHostOrder(natDetectionAttempts[i].c2Port); // SocketLayer::SendTo_PC( s4p5, (const char*) &c, 1, saOut, __FILE__, __LINE__ ); bsp.data = (char*) &c; bsp.length = 1; bsp.systemAddress = saOut; s4p5->Send(&bsp, _FILE_AND_LINE_); break; case STATE_TESTING_FULL_CONE_1: case STATE_TESTING_FULL_CONE_2: #ifdef NTDS_VERBOSE printf("Testing NAT_TYPE_FULL_CONE\n"); #endif rakPeerInterface->WriteOutOfBandHeader(&bs); bs.Write((unsigned char) ID_NAT_TYPE_DETECT); bs.Write((unsigned char) NAT_TYPE_FULL_CONE); // S2P3 sends to C1 (Different address, different port, to previously used port on client). If received, Full-cone nat. Done. (Else S2P3 potentially banned, do not use again). saOut=natDetectionAttempts[i].systemAddress; saOut.SetPortHostOrder(natDetectionAttempts[i].systemAddress.GetPort()); // SocketLayer::SendTo_PC( s2p3, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), saOut, __FILE__, __LINE__ ); bsp.data = (char*) bs.GetData(); bsp.length = bs.GetNumberOfBytesUsed(); bsp.systemAddress = saOut; s2p3->Send(&bsp, _FILE_AND_LINE_); break; case STATE_TESTING_ADDRESS_RESTRICTED_1: case STATE_TESTING_ADDRESS_RESTRICTED_2: #ifdef NTDS_VERBOSE printf("Testing NAT_TYPE_ADDRESS_RESTRICTED\n"); #endif rakPeerInterface->WriteOutOfBandHeader(&bs); bs.Write((unsigned char) ID_NAT_TYPE_DETECT); bs.Write((unsigned char) NAT_TYPE_ADDRESS_RESTRICTED); // S1P2 sends to C1 (Same address, different port, to previously used port on client). If received, address-restricted cone nat. Done. saOut=natDetectionAttempts[i].systemAddress; saOut.SetPortHostOrder(natDetectionAttempts[i].systemAddress.GetPort()); //SocketLayer::SendTo_PC( s1p2, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), saOut, __FILE__, __LINE__ ); bsp.data = (char*) bs.GetData(); bsp.length = bs.GetNumberOfBytesUsed(); bsp.systemAddress = saOut; s1p2->Send(&bsp, _FILE_AND_LINE_); break; case STATE_TESTING_PORT_RESTRICTED_1: case STATE_TESTING_PORT_RESTRICTED_2: // C1 sends to S3P4. If address of C1 as seen by S3P4 is the same as the address of C1 as seen by S1P1, then port-restricted cone nat. Done #ifdef NTDS_VERBOSE printf("Testing NAT_TYPE_PORT_RESTRICTED\n"); #endif bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_REQUEST); bs.Write(RakString::NonVariadic(s3p4Address)); bs.Write(s3p4->GetBoundAddress().GetPort()); rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); break; default: #ifdef NTDS_VERBOSE printf("Warning, exceeded final check STATE_TESTING_PORT_RESTRICTED_2.\nExpected that client would have sent NAT_TYPE_PORT_RESTRICTED on s3p4.\nDefaulting to Symmetric\n"); #endif bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_RESULT); bs.Write((unsigned char) NAT_TYPE_SYMMETRIC); rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false); natDetectionAttempts.RemoveAtIndexFast(i); i--; break; } } i++; } } PluginReceiveResult NatTypeDetectionServer::OnReceive(Packet *packet) { switch (packet->data[0]) { case ID_NAT_TYPE_DETECTION_REQUEST: OnDetectionRequest(packet); return RR_STOP_PROCESSING_AND_DEALLOCATE; } return RR_CONTINUE_PROCESSING; } void NatTypeDetectionServer::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) { (void) lostConnectionReason; (void) rakNetGUID; unsigned int i = GetDetectionAttemptIndex(systemAddress); if (i==(unsigned int)-1) return; natDetectionAttempts.RemoveAtIndexFast(i); } void NatTypeDetectionServer::OnDetectionRequest(Packet *packet) { unsigned int i = GetDetectionAttemptIndex(packet->systemAddress); RakNet::BitStream bsIn(packet->data, packet->length, false); bsIn.IgnoreBytes(1); bool isRequest=false; bsIn.Read(isRequest); if (isRequest) { if (i!=(unsigned int)-1) return; // Already in progress NATDetectionAttempt nda; nda.detectionState=STATE_NONE; nda.systemAddress=packet->systemAddress; nda.guid=packet->guid; bsIn.Read(nda.c2Port); nda.nextStateTime=0; nda.timeBetweenAttempts=rakPeerInterface->GetLastPing(nda.systemAddress)*3+50; natDetectionAttempts.Push(nda, _FILE_AND_LINE_); } else { if (i==(unsigned int)-1) return; // Unknown // They are done natDetectionAttempts.RemoveAtIndexFast(i); } } unsigned int NatTypeDetectionServer::GetDetectionAttemptIndex(const SystemAddress &sa) { for (unsigned int i=0; i < natDetectionAttempts.Size(); i++) { if (natDetectionAttempts[i].systemAddress==sa) return i; } return (unsigned int) -1; } unsigned int NatTypeDetectionServer::GetDetectionAttemptIndex(RakNetGUID guid) { for (unsigned int i=0; i < natDetectionAttempts.Size(); i++) { if (natDetectionAttempts[i].guid==guid) return i; } return (unsigned int) -1; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void NatTypeDetectionServer::DeallocRNS2RecvStruct(RNS2RecvStruct *s, const char *file, unsigned int line) { RakNet::OP_DELETE(s, file, line); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- RNS2RecvStruct *NatTypeDetectionServer::AllocRNS2RecvStruct(const char *file, unsigned int line) { return RakNet::OP_NEW(file,line); } void NatTypeDetectionServer::OnRNS2Recv(RNS2RecvStruct *recvStruct) { bufferedPacketsMutex.Lock(); bufferedPackets.Push(recvStruct,_FILE_AND_LINE_); bufferedPacketsMutex.Unlock(); } #endif // _RAKNET_SUPPORT_*