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