1 #include "NativeFeatureIncludes.h"
2 #if _RAKNET_SUPPORT_NatPunchthroughClient==1
3 
4 #include "NatPunchthroughClient.h"
5 #include "BitStream.h"
6 #include "MessageIdentifiers.h"
7 #include "RakPeerInterface.h"
8 #include "GetTime.h"
9 #include "PacketLogger.h"
10 
OnClientMessage(const char * msg)11 void NatPunchthroughDebugInterface_Printf::OnClientMessage(const char *msg)
12 {
13 	printf("%s\n", msg);
14 }
15 #if _RAKNET_SUPPORT_PacketLogger==1
OnClientMessage(const char * msg)16 void NatPunchthroughDebugInterface_PacketLogger::OnClientMessage(const char *msg)
17 {
18 	if (pl)
19 	{
20 		pl->WriteMiscellaneous("Nat", msg);
21 	}
22 }
23 #endif
24 
NatPunchthroughClient()25 NatPunchthroughClient::NatPunchthroughClient()
26 {
27 	natPunchthroughDebugInterface=0;
28 	mostRecentNewExternalPort=0;
29 	sp.nextActionTime=0;
30 }
~NatPunchthroughClient()31 NatPunchthroughClient::~NatPunchthroughClient()
32 {
33 	rakPeerInterface=0;
34 	Clear();
35 }
OpenNAT(RakNetGUID destination,SystemAddress facilitator)36 bool NatPunchthroughClient::OpenNAT(RakNetGUID destination, SystemAddress facilitator)
37 {
38 	if (rakPeerInterface->IsConnected(facilitator)==false)
39 		return false;
40 	// Already connected
41 	SystemAddress sa = rakPeerInterface->GetSystemAddressFromGuid(destination);
42 	if (sa!=UNASSIGNED_SYSTEM_ADDRESS && rakPeerInterface->IsConnected(sa,true,true) )
43 		return false;
44 
45 	SendPunchthrough(destination, facilitator);
46 	return true;
47 }
SetDebugInterface(NatPunchthroughDebugInterface * i)48 void NatPunchthroughClient::SetDebugInterface(NatPunchthroughDebugInterface *i)
49 {
50 	natPunchthroughDebugInterface=i;
51 }
GetUPNPExternalPort(void) const52 unsigned short NatPunchthroughClient::GetUPNPExternalPort(void) const
53 {
54 	return mostRecentNewExternalPort;
55 }
GetUPNPInternalPort(void) const56 unsigned short NatPunchthroughClient::GetUPNPInternalPort(void) const
57 {
58 	return rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).port;
59 }
GetUPNPInternalAddress(void) const60 RakNet::RakString NatPunchthroughClient::GetUPNPInternalAddress(void) const
61 {
62 	char dest[64];
63 	rakPeerInterface->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).ToString(false, dest);
64 	RakNet::RakString rs = dest;
65 	return rs;
66 }
Update(void)67 void NatPunchthroughClient::Update(void)
68 {
69 	RakNetTimeMS time = RakNet::GetTimeMS();
70 	if (sp.nextActionTime && sp.nextActionTime < time)
71 	{
72 		RakNetTimeMS delta = time - sp.nextActionTime;
73 		if (sp.testMode==SendPing::TESTING_INTERNAL_IPS)
74 		{
75 			SendOutOfBand(sp.internalIds[sp.attemptCount],ID_NAT_ESTABLISH_UNIDIRECTIONAL);
76 
77 			if (++sp.retryCount>=pc.UDP_SENDS_PER_PORT_INTERNAL)
78 			{
79 				++sp.attemptCount;
80 				sp.retryCount=0;
81 			}
82 
83 			if (sp.attemptCount>=pc.MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK)
84 			{
85 				sp.testMode=SendPing::WAITING_FOR_INTERNAL_IPS_RESPONSE;
86 				if (pc.INTERNAL_IP_WAIT_AFTER_ATTEMPTS>0)
87 				{
88 					sp.nextActionTime=time+pc.INTERNAL_IP_WAIT_AFTER_ATTEMPTS-delta;
89 				}
90 				else
91 				{
92 					sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FROM_FACILITATOR_PORT;
93 					sp.attemptCount=0;
94 				}
95 			}
96 			else
97 			{
98 				sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL-delta;
99 			}
100 		}
101 		else if (sp.testMode==SendPing::WAITING_FOR_INTERNAL_IPS_RESPONSE)
102 		{
103 			sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FROM_FACILITATOR_PORT;
104 			sp.attemptCount=0;
105 		}
106 
107 		if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_FROM_FACILITATOR_PORT)
108 		{
109 			SystemAddress sa;
110 			sa=sp.targetAddress;
111 			int port = sa.port+sp.attemptCount;
112 			sa.port=(unsigned short) port;
113 			SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL);
114 
115 			if (++sp.retryCount>=pc.UDP_SENDS_PER_PORT_EXTERNAL)
116 			{
117 				++sp.attemptCount;
118 				sp.retryCount=0;
119 				sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_BETWEEN_PORTS-delta;
120 			}
121 			else
122 			{
123 				sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL-delta;
124 			}
125 
126 			if (sp.attemptCount>=pc.MAX_PREDICTIVE_PORT_RANGE)
127 			{
128 				// From 1024 disabled, never helps as I've seen, but slows down the process by half
129 				//sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FROM_1024;
130 				//sp.attemptCount=0;
131 				sp.testMode=SendPing::WAITING_AFTER_ALL_ATTEMPTS;
132 				sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS-delta;
133 			}
134 		}
135 		else if (sp.testMode==SendPing::TESTING_EXTERNAL_IPS_FROM_1024)
136 		{
137 			SystemAddress sa;
138 			sa=sp.targetAddress;
139 			int port = 1024+sp.attemptCount;
140 			sa.port=(unsigned short) port;
141 			SendOutOfBand(sa,ID_NAT_ESTABLISH_UNIDIRECTIONAL);
142 
143 			if (++sp.retryCount>=pc.UDP_SENDS_PER_PORT_EXTERNAL)
144 			{
145 				++sp.attemptCount;
146 				sp.retryCount=0;
147 				sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_BETWEEN_PORTS-delta;
148 			}
149 			else
150 			{
151 				sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL-delta;
152 			}
153 
154 			if (sp.attemptCount>=pc.MAX_PREDICTIVE_PORT_RANGE)
155 			{
156 				if (natPunchthroughDebugInterface)
157 				{
158 					char ipAddressString[32];
159 					sp.targetAddress.ToString(true, ipAddressString);
160 					char guidString[128];
161 					sp.targetGuid.ToString(guidString);
162 					natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Likely bidirectional punchthrough failure to guid %s, system address %s.", guidString, ipAddressString));
163 				}
164 
165 				sp.testMode=SendPing::WAITING_AFTER_ALL_ATTEMPTS;
166 				sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS-delta;
167 			}
168 		}
169 		else if (sp.testMode==SendPing::WAITING_AFTER_ALL_ATTEMPTS)
170 		{
171 			// Failed. Tell the user
172 			OnPunchthroughFailure();
173 		}
174 
175 		if (sp.testMode==SendPing::PUNCHING_FIXED_PORT)
176 		{
177 //			RakAssert(rakPeerInterface->IsConnected(sp.targetAddress,true,true)==false);
178 			SendOutOfBand(sp.targetAddress,ID_NAT_ESTABLISH_BIDIRECTIONAL);
179 			if (++sp.retryCount>=sp.punchingFixedPortAttempts)
180 			{
181 				if (natPunchthroughDebugInterface)
182 				{
183 					char ipAddressString[32];
184 					sp.targetAddress.ToString(true, ipAddressString);
185 					char guidString[128];
186 					sp.targetGuid.ToString(guidString);
187 					natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Likely unidirectional punchthrough failure to guid %s, system address %s.", guidString, ipAddressString));
188 				}
189 
190 				sp.testMode=SendPing::WAITING_AFTER_ALL_ATTEMPTS;
191 				sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS-delta;
192 			}
193 			else
194 			{
195 				if ((sp.retryCount%pc.UDP_SENDS_PER_PORT_EXTERNAL)==0)
196 					sp.nextActionTime=time+pc.EXTERNAL_IP_WAIT_BETWEEN_PORTS-delta;
197 				else
198 					sp.nextActionTime=time+pc.TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL-delta;
199 			}
200 		}
201 	}
202 }
PushFailure(void)203 void NatPunchthroughClient::PushFailure(void)
204 {
205 	Packet *p = rakPeerInterface->AllocatePacket(sizeof(MessageID)+sizeof(unsigned char));
206 	p->data[0]=ID_NAT_PUNCHTHROUGH_FAILED;
207 	p->systemAddress=sp.targetAddress;
208 	p->systemAddress.systemIndex=(SystemIndex)-1;
209 	p->guid=sp.targetGuid;
210 	if (sp.weAreSender)
211 		p->data[1]=1;
212 	else
213 		p->data[1]=0;
214 	p->bypassPlugins=true;
215 	rakPeerInterface->PushBackPacket(p, true);
216 }
OnPunchthroughFailure(void)217 void NatPunchthroughClient::OnPunchthroughFailure(void)
218 {
219 	if (pc.retryOnFailure==false)
220 	{
221 		if (natPunchthroughDebugInterface)
222 		{
223 			char ipAddressString[32];
224 			sp.targetAddress.ToString(true, ipAddressString);
225 			char guidString[128];
226 			sp.targetGuid.ToString(guidString);
227 			natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Failed punchthrough once. Returning failure to guid %s, system address %s to user.", guidString, ipAddressString));
228 		}
229 
230 		PushFailure();
231 		OnReadyForNextPunchthrough();
232 		return;
233 	}
234 
235 	unsigned int i;
236 	for (i=0; i < failedAttemptList.Size(); i++)
237 	{
238 		if (failedAttemptList[i].guid==sp.targetGuid)
239 		{
240 			if (natPunchthroughDebugInterface)
241 			{
242 				char ipAddressString[32];
243 				sp.targetAddress.ToString(true, ipAddressString);
244 				char guidString[128];
245 				sp.targetGuid.ToString(guidString);
246 				natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Failed punchthrough twice. Returning failure to guid %s, system address %s to user.", guidString, ipAddressString));
247 			}
248 
249 			// Failed a second time, so return failed to user
250 			PushFailure();
251 
252 			OnReadyForNextPunchthrough();
253 
254 			failedAttemptList.RemoveAtIndexFast(i);
255 			return;
256 		}
257 	}
258 
259 	if (rakPeerInterface->IsConnected(sp.facilitator)==false)
260 	{
261 		if (natPunchthroughDebugInterface)
262 		{
263 			char ipAddressString[32];
264 			sp.targetAddress.ToString(true, ipAddressString);
265 			char guidString[128];
266 			sp.targetGuid.ToString(guidString);
267 			natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Not connected to facilitator, so cannot retry punchthrough after first failure. Returning failure onj guid %s, system address %s to user.", guidString, ipAddressString));
268 		}
269 
270 		// Failed, and can't try again because no facilitator
271 		PushFailure();
272 		return;
273 	}
274 
275 	if (natPunchthroughDebugInterface)
276 	{
277 		char ipAddressString[32];
278 		sp.targetAddress.ToString(true, ipAddressString);
279 		char guidString[128];
280 		sp.targetGuid.ToString(guidString);
281 		natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("First punchthrough failure on guid %s, system address %s. Reattempting.", guidString, ipAddressString));
282 	}
283 
284 	// Failed the first time. Add to the failure queue and try again
285 	AddrAndGuid aag;
286 	aag.addr=sp.targetAddress;
287 	aag.guid=sp.targetGuid;
288 	failedAttemptList.Push(aag, __FILE__, __LINE__);
289 
290 	// Tell the server we are ready
291 	OnReadyForNextPunchthrough();
292 
293 	// If we are the sender, try again, immediately if possible, else added to the queue on the faciltiator
294 	if (sp.weAreSender)
295 		SendPunchthrough(sp.targetGuid, sp.facilitator);
296 }
OnReceive(Packet * packet)297 PluginReceiveResult NatPunchthroughClient::OnReceive(Packet *packet)
298 {
299 	switch (packet->data[0])
300 	{
301 	case ID_NAT_GET_MOST_RECENT_PORT:
302 		{
303 			OnGetMostRecentPort(packet);
304 			return RR_STOP_PROCESSING_AND_DEALLOCATE;
305 		}
306 	case ID_NAT_PUNCHTHROUGH_FAILED:
307 	case ID_NAT_PUNCHTHROUGH_SUCCEEDED:
308 		return RR_STOP_PROCESSING_AND_DEALLOCATE;
309 	case ID_OUT_OF_BAND_INTERNAL:
310 		if (packet->length>=2 &&
311 			(packet->data[1]==ID_NAT_ESTABLISH_UNIDIRECTIONAL || packet->data[1]==ID_NAT_ESTABLISH_BIDIRECTIONAL) &&
312 			sp.nextActionTime!=0)
313 		{
314 			RakNet::BitStream bs(packet->data,packet->length,false);
315 			bs.IgnoreBytes(2);
316 			uint16_t sessionId;
317 			bs.Read(sessionId);
318 //			RakAssert(sessionId<100);
319 			if (sessionId!=sp.sessionId)
320 				break;
321 
322 			char ipAddressString[32];
323 			packet->systemAddress.ToString(true,ipAddressString);
324 			// sp.targetGuid==packet->guid is because the internal IP addresses reported may include loopbacks not reported by RakPeer::IsLocalIP()
325 			if (packet->data[1]==ID_NAT_ESTABLISH_UNIDIRECTIONAL && sp.targetGuid==packet->guid)
326 			{
327 				if (natPunchthroughDebugInterface)
328 				{
329 					char guidString[128];
330 					sp.targetGuid.ToString(guidString);
331 					natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Received ID_NAT_ESTABLISH_UNIDIRECTIONAL from guid %s, system address %s.", guidString, ipAddressString));
332 				}
333 				if (sp.testMode!=SendPing::PUNCHING_FIXED_PORT)
334 				{
335 					sp.testMode=SendPing::PUNCHING_FIXED_PORT;
336 					sp.retryCount+=sp.attemptCount*pc.UDP_SENDS_PER_PORT_EXTERNAL;
337 					sp.targetAddress=packet->systemAddress;
338 //					RakAssert(rakPeerInterface->IsConnected(sp.targetAddress,true,true)==false);
339 					// Keeps trying until the other side gives up too, in case it is unidirectional
340 					sp.punchingFixedPortAttempts=pc.UDP_SENDS_PER_PORT_EXTERNAL*pc.MAX_PREDICTIVE_PORT_RANGE;
341 				}
342 
343 				SendOutOfBand(sp.targetAddress,ID_NAT_ESTABLISH_BIDIRECTIONAL);
344 			}
345 			else if (packet->data[1]==ID_NAT_ESTABLISH_BIDIRECTIONAL &&
346 				sp.targetGuid==packet->guid)
347 			{
348 				// They send back our port
349 				bs.Read(mostRecentNewExternalPort);
350 
351 				SendOutOfBand(packet->systemAddress,ID_NAT_ESTABLISH_BIDIRECTIONAL);
352 
353 				// Tell the user about the success
354 				sp.targetAddress=packet->systemAddress;
355 				PushSuccess();
356 				OnReadyForNextPunchthrough();
357 //				RakAssert(rakPeerInterface->IsConnected(sp.targetAddress,true,true)==false);
358 				bool removedFromFailureQueue=RemoveFromFailureQueue();
359 
360 				if (natPunchthroughDebugInterface)
361 				{
362 					char guidString[128];
363 					sp.targetGuid.ToString(guidString);
364 					if (removedFromFailureQueue)
365 						natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough to guid %s, system address %s succeeded on 2nd attempt.", guidString, ipAddressString));
366 					else
367 						natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough to guid %s, system address %s succeeded on 1st attempt.", guidString, ipAddressString));
368 				}
369 			}
370 
371 	//		mostRecentNewExternalPort=packet->systemAddress.port;
372 		}
373 		return RR_STOP_PROCESSING_AND_DEALLOCATE;
374 	case ID_NAT_ALREADY_IN_PROGRESS:
375 		{
376 			RakNet::BitStream incomingBs(packet->data, packet->length, false);
377 			incomingBs.IgnoreBytes(sizeof(MessageID));
378 			RakNetGUID targetGuid;
379 			incomingBs.Read(targetGuid);
380 			if (natPunchthroughDebugInterface)
381 			{
382 				char guidString[128];
383 				targetGuid.ToString(guidString);
384 				natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough retry to guid %s failed due to ID_NAT_ALREADY_IN_PROGRESS. Returning failure.", guidString));
385 			}
386 
387 		}
388 		break;
389 	case ID_NAT_TARGET_NOT_CONNECTED:
390 	case ID_NAT_CONNECTION_TO_TARGET_LOST:
391 	case ID_NAT_TARGET_UNRESPONSIVE:
392 		{
393 			const char *reason;
394 			if (packet->data[0]==ID_NAT_TARGET_NOT_CONNECTED)
395 				reason="ID_NAT_TARGET_NOT_CONNECTED";
396 			else if (packet->data[0]==ID_NAT_CONNECTION_TO_TARGET_LOST)
397 				reason="ID_NAT_CONNECTION_TO_TARGET_LOST";
398 			else
399 				reason="ID_NAT_TARGET_UNRESPONSIVE";
400 
401 			RakNet::BitStream incomingBs(packet->data, packet->length, false);
402 			incomingBs.IgnoreBytes(sizeof(MessageID));
403 
404 			RakNetGUID targetGuid;
405 			incomingBs.Read(targetGuid);
406 			if (packet->data[0]==ID_NAT_CONNECTION_TO_TARGET_LOST ||
407 				packet->data[0]==ID_NAT_TARGET_UNRESPONSIVE)
408 			{
409 				uint16_t sessionId;
410 				incomingBs.Read(sessionId);
411 				if (sessionId!=sp.sessionId)
412 					break;
413 			}
414 
415 			unsigned int i;
416 			for (i=0; i < failedAttemptList.Size(); i++)
417 			{
418 				if (failedAttemptList[i].guid==targetGuid)
419 				{
420 					if (natPunchthroughDebugInterface)
421 					{
422 						char guidString[128];
423 						targetGuid.ToString(guidString);
424 						natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough retry to guid %s failed due to %s.", guidString, reason));
425 
426 					}
427 
428 					// If the retry target is not connected, or loses connection, or is not responsive, then previous failures cannot be retried.
429 
430 					// Don't need to return failed, the other messages indicate failure anyway
431 					/*
432 					Packet *p = rakPeerInterface->AllocatePacket(sizeof(MessageID));
433 					p->data[0]=ID_NAT_PUNCHTHROUGH_FAILED;
434 					p->systemAddress=failedAttemptList[i].addr;
435 					p->systemAddress.systemIndex=(SystemIndex)-1;
436 					p->guid=failedAttemptList[i].guid;
437 					rakPeerInterface->PushBackPacket(p, false);
438 					*/
439 
440 					failedAttemptList.RemoveAtIndexFast(i);
441 					break;
442 				}
443 			}
444 
445 			if (natPunchthroughDebugInterface)
446 			{
447 				char guidString[128];
448 				targetGuid.ToString(guidString);
449 				natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Punchthrough attempt to guid %s failed due to %s.", guidString, reason));
450 			}
451 
452 			// Stop trying punchthrough
453 			sp.nextActionTime=0;
454 
455 			/*
456 			RakNet::BitStream bs(packet->data, packet->length, false);
457 			bs.IgnoreBytes(sizeof(MessageID));
458 			RakNetGUID failedSystem;
459 			bs.Read(failedSystem);
460 			bool deletedFirst=false;
461 			unsigned int i=0;
462 			while (i < pendingOpenNAT.Size())
463 			{
464 				if (pendingOpenNAT[i].destination==failedSystem)
465 				{
466 					if (i==0)
467 						deletedFirst=true;
468 					pendingOpenNAT.RemoveAtIndex(i);
469 				}
470 				else
471 					i++;
472 			}
473 			// Failed while in progress. Go to next in attempt queue
474 			if (deletedFirst && pendingOpenNAT.Size())
475 			{
476 				SendPunchthrough(pendingOpenNAT[0].destination, pendingOpenNAT[0].facilitator);
477 				sp.nextActionTime=0;
478 			}
479 			*/
480 		}
481 		break;
482 	case ID_TIMESTAMP:
483 		if (packet->data[sizeof(MessageID)+sizeof(RakNetTime)]==ID_NAT_CONNECT_AT_TIME)
484 		{
485 			OnConnectAtTime(packet);
486 			return RR_STOP_PROCESSING_AND_DEALLOCATE;
487 		}
488 		break;
489 	}
490 	return RR_CONTINUE_PROCESSING;
491 }
492 /*
493 void NatPunchthroughClient::ProcessNextPunchthroughQueue(void)
494 {
495 	// Go to the next attempt
496 	if (pendingOpenNAT.Size())
497 		pendingOpenNAT.RemoveAtIndex(0);
498 
499 	// Do next punchthrough attempt
500 	if (pendingOpenNAT.Size())
501 		SendPunchthrough(pendingOpenNAT[0].destination, pendingOpenNAT[0].facilitator);
502 
503 	sp.nextActionTime=0;
504 }
505 */
OnConnectAtTime(Packet * packet)506 void NatPunchthroughClient::OnConnectAtTime(Packet *packet)
507 {
508 //	RakAssert(sp.nextActionTime==0);
509 
510 	RakNet::BitStream bs(packet->data, packet->length, false);
511 	bs.IgnoreBytes(sizeof(MessageID));
512 	bs.Read(sp.nextActionTime);
513 	bs.IgnoreBytes(sizeof(MessageID));
514 	bs.Read(sp.sessionId);
515 	bs.Read(sp.targetAddress);
516 	//RakAssert(rakPeerInterface->IsConnected(sp.targetAddress,true,true)==false);
517 	int j,k;
518 	k=0;
519 	for (j=0; j < MAXIMUM_NUMBER_OF_INTERNAL_IDS; j++)
520 	{
521 		SystemAddress id;
522 		bs.Read(id);
523 		char str[32];
524 		id.ToString(false,str);
525 		if (rakPeerInterface->IsLocalIP(str)==false)
526 			sp.internalIds[k++]=id;
527 	}
528 	sp.attemptCount=0;
529 	sp.retryCount=0;
530 	if (pc.MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK>0)
531 	{
532 		sp.testMode=SendPing::TESTING_INTERNAL_IPS;
533 	}
534 	else
535 	{
536 		sp.testMode=SendPing::TESTING_EXTERNAL_IPS_FROM_FACILITATOR_PORT;
537 	}
538 	bs.Read(sp.targetGuid);
539 	bs.Read(sp.weAreSender);
540 
541 	//RakAssert(rakPeerInterface->IsConnected(sp.targetAddress,true,true)==false);
542 }
SendTTL(SystemAddress sa)543 void NatPunchthroughClient::SendTTL(SystemAddress sa)
544 {
545 	if (sa==UNASSIGNED_SYSTEM_ADDRESS)
546 		return;
547 	if (sa.port==0)
548 		return;
549 
550 	char ipAddressString[32];
551 	sa.ToString(false, ipAddressString);
552 	rakPeerInterface->SendTTL(ipAddressString,sa.port, 3);
553 }
SendOutOfBand(SystemAddress sa,MessageID oobId)554 void NatPunchthroughClient::SendOutOfBand(SystemAddress sa, MessageID oobId)
555 {
556 	if (sa==UNASSIGNED_SYSTEM_ADDRESS)
557 		return;
558 	if (sa.port==0)
559 		return;
560 
561 	//RakAssert(rakPeerInterface->IsConnected(sp.targetAddress,true,true)==false);
562 
563 	RakNet::BitStream oob;
564 	oob.Write(oobId);
565 	oob.Write(sp.sessionId);
566 //	RakAssert(sp.sessionId<100);
567 	if (oobId==ID_NAT_ESTABLISH_BIDIRECTIONAL)
568 		oob.Write(sa.port);
569 	char ipAddressString[32];
570 	sa.ToString(false, ipAddressString);
571 	rakPeerInterface->SendOutOfBand((const char*) ipAddressString,sa.port,(const char*) oob.GetData(),oob.GetNumberOfBytesUsed());
572 
573 	if (natPunchthroughDebugInterface)
574 	{
575 		sa.ToString(true,ipAddressString);
576 		char guidString[128];
577 		sp.targetGuid.ToString(guidString);
578 
579 		if (oobId==ID_NAT_ESTABLISH_UNIDIRECTIONAL)
580 			natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Sent OOB ID_NAT_ESTABLISH_UNIDIRECTIONAL to guid %s, system address %s.", guidString, ipAddressString));
581 		else
582 			natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Sent OOB ID_NAT_ESTABLISH_BIDIRECTIONAL to guid %s, system address %s.", guidString, ipAddressString));
583 	}
584 }
OnNewConnection(SystemAddress systemAddress,RakNetGUID rakNetGUID,bool isIncoming)585 void NatPunchthroughClient::OnNewConnection(SystemAddress systemAddress, RakNetGUID rakNetGUID, bool isIncoming)
586 {
587 	(void) rakNetGUID;
588 	(void) isIncoming;
589 
590 	// Try to track new port mappings on the router. Not reliable, but better than nothing.
591 	SystemAddress ourExternalId = rakPeerInterface->GetExternalID(systemAddress);
592 	if (ourExternalId!=UNASSIGNED_SYSTEM_ADDRESS)
593 		mostRecentNewExternalPort=ourExternalId.port;
594 }
595 
OnClosedConnection(SystemAddress systemAddress,RakNetGUID rakNetGUID,PI2_LostConnectionReason lostConnectionReason)596 void NatPunchthroughClient::OnClosedConnection(SystemAddress systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
597 {
598 	(void) systemAddress;
599 	(void) rakNetGUID;
600 	(void) lostConnectionReason;
601 
602 	if (sp.facilitator==systemAddress)
603 	{
604 		// If we lose the connection to the facilitator, all previous failures not currently in progress are returned as such
605 		unsigned int i=0;
606 		while (i < failedAttemptList.Size())
607 		{
608 			if (sp.nextActionTime!=0 && sp.targetGuid==failedAttemptList[i].guid)
609 			{
610 				i++;
611 				continue;
612 			}
613 
614 			PushFailure();
615 
616 			failedAttemptList.RemoveAtIndexFast(i);
617 		}
618 	}
619 
620 	/*
621 	(void) lostConnectionReason;
622 
623 	bool deletedFirst=false;
624 	unsigned int i=0;
625 	while (i < pendingOpenNAT.Size())
626 	{
627 		if (pendingOpenNAT[i].facilitator==systemAddress)
628 		{
629 			if (natPunchthroughDebugInterface)
630 			{
631 				if (lostConnectionReason==LCR_CLOSED_BY_USER)
632 					natPunchthroughDebugInterface->OnClientMessage("Nat server connection lost. Reason=LCR_CLOSED_BY_USER\n");
633 				else if (lostConnectionReason==LCR_DISCONNECTION_NOTIFICATION)
634 					natPunchthroughDebugInterface->OnClientMessage("Nat server connection lost. Reason=LCR_CLOSED_BY_USER\n");
635 				else if (lostConnectionReason==LCR_CONNECTION_LOST)
636 					natPunchthroughDebugInterface->OnClientMessage("Nat server connection lost. Reason=LCR_CONNECTION_LOST\n");
637 			}
638 
639 			// Request failed because connection to server lost before remote system ping attempt occurred
640 			Packet *p = rakPeerInterface->AllocatePacket(sizeof(MessageID));
641 			p->data[0]=ID_NAT_CONNECTION_TO_TARGET_LOST;
642 			p->systemAddress=systemAddress;
643 			p->systemAddress.systemIndex=(SystemIndex)-1;
644 			p->guid=rakNetGUID;
645 			rakPeerInterface->PushBackPacket(p, false);
646 			if (i==0)
647 				deletedFirst;
648 
649 			pendingOpenNAT.RemoveAtIndex(i);
650 		}
651 		else
652 			i++;
653 	}
654 
655 	// Lost connection to facilitator while an attempt was in progress. Give up on that attempt, and try the next in the queue.
656 	if (deletedFirst && pendingOpenNAT.Size())
657 	{
658 		SendPunchthrough(pendingOpenNAT[0].destination, pendingOpenNAT[0].facilitator);
659 	}
660 	*/
661 }
OnGetMostRecentPort(Packet * packet)662 void NatPunchthroughClient::OnGetMostRecentPort(Packet *packet)
663 {
664 	RakNet::BitStream incomingBs(packet->data,packet->length,false);
665 	incomingBs.IgnoreBytes(sizeof(MessageID));
666 	uint16_t sessionId;
667 	incomingBs.Read(sessionId);
668 
669 	RakNet::BitStream outgoingBs;
670 	outgoingBs.Write((MessageID)ID_NAT_GET_MOST_RECENT_PORT);
671 	outgoingBs.Write(sessionId);
672 	if (mostRecentNewExternalPort==0)
673 		mostRecentNewExternalPort=rakPeerInterface->GetExternalID(packet->systemAddress).port;
674 	outgoingBs.Write(mostRecentNewExternalPort);
675 	rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->systemAddress,false);
676 	sp.facilitator=packet->systemAddress;
677 }
678 /*
679 unsigned int NatPunchthroughClient::GetPendingOpenNATIndex(RakNetGUID destination, SystemAddress facilitator)
680 {
681 	unsigned int i;
682 	for (i=0; i < pendingOpenNAT.Size(); i++)
683 	{
684 		if (pendingOpenNAT[i].destination==destination && pendingOpenNAT[i].facilitator==facilitator)
685 			return i;
686 	}
687 	return (unsigned int) -1;
688 }
689 */
SendPunchthrough(RakNetGUID destination,SystemAddress facilitator)690 void NatPunchthroughClient::SendPunchthrough(RakNetGUID destination, SystemAddress facilitator)
691 {
692 	RakNet::BitStream outgoingBs;
693 	outgoingBs.Write((MessageID)ID_NAT_PUNCHTHROUGH_REQUEST);
694 	outgoingBs.Write(destination);
695 	rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,facilitator,false);
696 
697 //	RakAssert(rakPeerInterface->GetSystemAddressFromGuid(destination)==UNASSIGNED_SYSTEM_ADDRESS);
698 
699 	if (natPunchthroughDebugInterface)
700 	{
701 		char guidString[128];
702 		destination.ToString(guidString);
703 		natPunchthroughDebugInterface->OnClientMessage(RakNet::RakString("Starting ID_NAT_PUNCHTHROUGH_REQUEST to guid %s.", guidString));
704 	}
705 }
OnAttach(void)706 void NatPunchthroughClient::OnAttach(void)
707 {
708 	Clear();
709 }
OnDetach(void)710 void NatPunchthroughClient::OnDetach(void)
711 {
712 	Clear();
713 }
OnRakPeerShutdown(void)714 void NatPunchthroughClient::OnRakPeerShutdown(void)
715 {
716 	Clear();
717 }
Clear(void)718 void NatPunchthroughClient::Clear(void)
719 {
720 	OnReadyForNextPunchthrough();
721 
722 	failedAttemptList.Clear(false, __FILE__,__LINE__);
723 }
GetPunchthroughConfiguration(void)724 PunchthroughConfiguration* NatPunchthroughClient::GetPunchthroughConfiguration(void)
725 {
726 	return &pc;
727 }
OnReadyForNextPunchthrough(void)728 void NatPunchthroughClient::OnReadyForNextPunchthrough(void)
729 {
730 	if (rakPeerInterface==0)
731 		return;
732 
733 	sp.nextActionTime=0;
734 
735 	RakNet::BitStream outgoingBs;
736 	outgoingBs.Write((MessageID)ID_NAT_CLIENT_READY);
737 	rakPeerInterface->Send(&outgoingBs,HIGH_PRIORITY,RELIABLE_ORDERED,0,sp.facilitator,false);
738 }
739 
PushSuccess(void)740 void NatPunchthroughClient::PushSuccess(void)
741 {
742 //	RakAssert(rakPeerInterface->IsConnected(sp.targetAddress,true,true)==false);
743 
744 	Packet *p = rakPeerInterface->AllocatePacket(sizeof(MessageID)+sizeof(unsigned char));
745 	p->data[0]=ID_NAT_PUNCHTHROUGH_SUCCEEDED;
746 	p->systemAddress=sp.targetAddress;
747 	p->systemAddress.systemIndex=(SystemIndex)-1;
748 	p->guid=sp.targetGuid;
749 	if (sp.weAreSender)
750 		p->data[1]=1;
751 	else
752 		p->data[1]=0;
753 	p->bypassPlugins=true;
754 	rakPeerInterface->PushBackPacket(p, true);
755 }
756 
RemoveFromFailureQueue(void)757 bool NatPunchthroughClient::RemoveFromFailureQueue(void)
758 {
759 	unsigned int i;
760 	for (i=0; i < failedAttemptList.Size(); i++)
761 	{
762 		if (failedAttemptList[i].guid==sp.targetGuid)
763 		{
764 			// Remove from failure queue
765 			failedAttemptList.RemoveAtIndexFast(i);
766 			return true;
767 		}
768 	}
769 	return false;
770 }
771 
772 #endif // _RAKNET_SUPPORT_*
773 
774