int RakNet::NatTypeRecvFrom(char *data, SOCKET socket, SystemAddress &sender)
{
	sockaddr_in sa;
	socklen_t len2;
	const int flag=0;
	len2 = sizeof( sa );
	sa.sin_family = AF_INET;
	sa.sin_port=0;
	int len = recvfrom( socket, data, MAXIMUM_MTU_SIZE, flag, ( sockaddr* ) & sa, ( socklen_t* ) & len2 );
	if (len>0)
	{
		sender.address.addr4.sin_family=AF_INET;
		sender.address.addr4.sin_addr.s_addr = sa.sin_addr.s_addr;
		//sender.SetPort( ntohs( sa.sin_port ) );
		sender.SetPort( ntohs( sa.sin_port ) );
	}
	return len;
}
void NatTypeDetectionServer::Update(void)
{
	int i=0;
	RakNet::TimeMS time = RakNet::GetTimeMS();
	RakNet::BitStream bs;
	SystemAddress boundAddress;

	// 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)
				{
					printf("Determined client is symmetric\n");
					bs.Write((unsigned char) NAT_TYPE_SYMMETRIC);
				}
				else
				{
					// else port restricted
					printf("Determined client is port restricted\n");
					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)
		{
			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;
				printf("Testing NAT_TYPE_NONE\n");
				// S4P5 sends to C2. If arrived, no NAT. Done. (Else S4P5 potentially banned, do not use again).
				saOut=natDetectionAttempts[i].systemAddress;
				saOut.SetPort(natDetectionAttempts[i].c2Port);
				SocketLayer::SendTo_PC( s4p5, (const char*) &c, 1, saOut, __FILE__, __LINE__  );
				break;
			case STATE_TESTING_FULL_CONE_1:
			case STATE_TESTING_FULL_CONE_2:
				printf("Testing NAT_TYPE_FULL_CONE\n");
				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.SetPort(natDetectionAttempts[i].systemAddress.GetPort());
				SocketLayer::SendTo_PC( s2p3, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), saOut, __FILE__, __LINE__  );
				break;
			case STATE_TESTING_ADDRESS_RESTRICTED_1:
			case STATE_TESTING_ADDRESS_RESTRICTED_2:
				printf("Testing NAT_TYPE_ADDRESS_RESTRICTED\n");
				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.SetPort(natDetectionAttempts[i].systemAddress.GetPort());
				SocketLayer::SendTo_PC( s1p2, (const char*) bs.GetData(), bs.GetNumberOfBytesUsed(), saOut, __FILE__, __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
				printf("Testing NAT_TYPE_PORT_RESTRICTED\n");
				bs.Write((unsigned char) ID_NAT_TYPE_DETECTION_REQUEST);
				bs.Write(RakString::NonVariadic(s3p4Address));
				bs.Write(s3p4Port);
				rakPeerInterface->Send(&bs,HIGH_PRIORITY,RELIABLE,0,natDetectionAttempts[i].systemAddress,false);
				break;
			default:
				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");
				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++;
	}
}
Esempio n. 3
0
PluginReceiveResult Router2::OnReceive(Packet *packet)
{
	SystemAddress sa;
	RakNet::BitStream bs(packet->data,packet->length,false);
	if (packet->data[0]==ID_ROUTER_2_INTERNAL)
	{
		switch (packet->data[1])
		{
		case ID_ROUTER_2_QUERY_FORWARDING:
			{
				OnQueryForwarding(packet);
				return RR_STOP_PROCESSING_AND_DEALLOCATE;
			}
		case ID_ROUTER_2_REPLY_FORWARDING:
			{
				OnQueryForwardingReply(packet);
				return RR_STOP_PROCESSING_AND_DEALLOCATE;
			}
		case ID_ROUTER_2_REQUEST_FORWARDING:
			{

                if (debugInterface)
                {
                    char buff[512];
                    char buff2[32];
                    packet->systemAddress.ToString(true,buff2);
                    debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REQUEST_FORWARDING on ip %s from %I64d, ",
                        buff2,packet->guid.g));
                }

				OnRequestForwarding(packet);
				return RR_STOP_PROCESSING_AND_DEALLOCATE;
			}
		case ID_ROUTER_2_INCREASE_TIMEOUT:
			{
				/// The routed system wants more time to stay alive on no communication, in case the router drops or crashes
				rakPeerInterface->SetTimeoutTime(rakPeerInterface->GetTimeoutTime(packet->systemAddress)+10000, packet->systemAddress);
				return RR_STOP_PROCESSING_AND_DEALLOCATE;
			}
		}
	}
	else if (packet->data[0]==ID_OUT_OF_BAND_INTERNAL && packet->length>=2)
	{
		switch (packet->data[1])
		{
			case ID_ROUTER_2_REPLY_TO_SENDER_PORT:
				{
					RakNet::BitStream bsOut;
					bsOut.Write(packet->guid);
					SendOOBFromRakNetPort(ID_ROUTER_2_MINI_PUNCH_REPLY, &bsOut, packet->systemAddress);

					if (debugInterface)
					{
						char buff[512];
						char buff2[32];
						sa.ToString(false,buff2);
						debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_TO_SENDER_PORT %i on address %s, replying with ID_ROUTER_2_MINI_PUNCH_REPLY at %s:%i\n", sa.GetPort(), buff2, _FILE_AND_LINE_));

//						packet->systemAddress.ToString(true,buff2);
//						debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_TO_SENDER_PORT on address %s (%I64d), "
//                                       "replying with ID_ROUTER_2_MINI_PUNCH_REPLY at %s:%i\n", buff2,packet->guid.g, __FILE__, __LINE__));
					}

					return RR_STOP_PROCESSING_AND_DEALLOCATE;
				}
			case ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT:
				{
					RakNet::BitStream bsOut;
					bsOut.Write(packet->guid);
					bs.IgnoreBytes(2);
					sa=packet->systemAddress;
					unsigned short port;
					bs.Read(port);
					sa.SetPort(port);
					RakAssert(sa.GetPort()!=0);
					SendOOBFromRakNetPort(ID_ROUTER_2_MINI_PUNCH_REPLY, &bsOut, sa);

					if (debugInterface)
					{
						char buff[512];
						char buff2[32];
						sa.ToString(false,buff2);
						debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT %i on address %s, "
                                        "replying with ID_ROUTER_2_MINI_PUNCH_REPLY at %s:%i\n", sa.GetPort(), buff2, __FILE__, __LINE__));
					}

					return RR_STOP_PROCESSING_AND_DEALLOCATE;
				}
			case ID_ROUTER_2_MINI_PUNCH_REPLY:
				OnMiniPunchReply(packet);
				return RR_STOP_PROCESSING_AND_DEALLOCATE;
			case ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE:
				OnMiniPunchReplyBounce(packet);
				return RR_STOP_PROCESSING_AND_DEALLOCATE;
		}
	}
	else if (packet->data[0]==ID_ROUTER_2_FORWARDING_ESTABLISHED)
	{
//		printf("Got ID_ROUTER_2_FORWARDING_ESTABLISHED\n");
		if (OnForwardingSuccess(packet)==false)
			return RR_STOP_PROCESSING_AND_DEALLOCATE;
	}
	else if (packet->data[0]==ID_ROUTER_2_REROUTED)
	{
		OnRerouted(packet);
	}
	else if (packet->data[0]==ID_CONNECTION_REQUEST_ACCEPTED)
	{
		unsigned int forwardingIndex;
		forwardedConnectionListMutex.Lock();
		for (forwardingIndex=0; forwardingIndex < forwardedConnectionList.Size(); forwardingIndex++)
		{
			if (forwardedConnectionList[forwardingIndex].endpointGuid==packet->guid && forwardedConnectionList[forwardingIndex].weInitiatedForwarding)
				break;
		}

		if (forwardingIndex<forwardedConnectionList.Size())
		{
            forwardedConnectionListMutex.Unlock();

			// We connected to this system through a forwarding system
			// Have the endpoint take longer to drop us, in case the intermediary system drops
			RakNet::BitStream bsOut;
			bsOut.Write((MessageID)ID_ROUTER_2_INTERNAL);
			bsOut.Write((unsigned char) ID_ROUTER_2_INCREASE_TIMEOUT);
			rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE,0,packet->guid,false);

			if (debugInterface)
			{
				char buff[512];
				debugInterface->ShowDiagnostic(FormatStringTS(buff,"Got ID_CONNECTION_REQUEST_ACCEPTED, "
                    "sending ID_ROUTER_2_INCREASE_TIMEOUT to the %I64d at %s:%i\n", packet->guid.g, __FILE__, __LINE__));
			}

			// Also take longer ourselves
			rakPeerInterface->SetTimeoutTime(rakPeerInterface->GetTimeoutTime(packet->systemAddress)+10000, packet->systemAddress);
		}
        else  {
        	// ~Gwynn: Fix for Receive hanging up problem on Windows XP
		    // See http://blog.delphi-jedi.net/2008/04/23/the-case-of-the-unexplained-dead-lock-in-a-single-thread/ for details
            forwardedConnectionListMutex.Unlock();
        }
	}
	else if (packet->data[0]==ID_ROUTER_2_FORWARDING_NO_PATH)
	{
		if (packet->wasGeneratedLocally==false)
			return RR_STOP_PROCESSING_AND_DEALLOCATE;
	}

	return RR_CONTINUE_PROCESSING;
}
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.SetPort(connectionAttempt->recipient->mostRecentPort);
                senderTargetAddress.SetPort(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());
        }

    }
}