| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- /*
- * 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.
- *
- */
- /// \file
- /// \brief Contains the NAT-punchthrough plugin for the client.
- ///
- #include "NativeFeatureIncludes.h"
- #if _RAKNET_SUPPORT_NatPunchthroughClient==1
- #ifndef __NAT_PUNCHTHROUGH_CLIENT_H
- #define __NAT_PUNCHTHROUGH_CLIENT_H
- #include "RakNetTypes.h"
- #include "Export.h"
- #include "PluginInterface2.h"
- #include "PacketPriority.h"
- #include "SocketIncludes.h"
- #include "DS_List.h"
- #include "RakString.h"
- #include "DS_Queue.h"
- // Trendnet TEW-632BRP sometimes starts at port 1024 and increments sequentially.
- // Zonnet zsr1134we. Replies go out on the net, but are always absorbed by the remote router??
- // Dlink ebr2310 to Trendnet ok
- // Trendnet TEW-652BRP to Trendnet 632BRP OK
- // Trendnet TEW-632BRP to Trendnet 632BRP OK
- // Buffalo WHR-HP-G54 OK
- // Netgear WGR614 ok
- namespace RakNet
- {
- /// Forward declarations
- class RakPeerInterface;
- struct Packet;
- #if _RAKNET_SUPPORT_PacketLogger==1
- class PacketLogger;
- #endif
- /// \ingroup NAT_PUNCHTHROUGH_GROUP
- struct RAK_DLL_EXPORT PunchthroughConfiguration
- {
- /// internal: (15 ms * 2 tries + 30 wait) * 5 ports * 8 players = 2.4 seconds
- /// external: (50 ms * 8 sends + 200 wait) * 2 port * 8 players = 9.6 seconds
- /// Total: 8 seconds
- PunchthroughConfiguration() {
- TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL=15;
- TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL=50;
- UDP_SENDS_PER_PORT_INTERNAL=2;
- UDP_SENDS_PER_PORT_EXTERNAL=8;
- INTERNAL_IP_WAIT_AFTER_ATTEMPTS=30;
- MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK=5; /// set to 0 to not do lan connects
- MAX_PREDICTIVE_PORT_RANGE=2;
- EXTERNAL_IP_WAIT_BETWEEN_PORTS=200;
- EXTERNAL_IP_WAIT_AFTER_FIRST_TTL=100;
- EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS=EXTERNAL_IP_WAIT_BETWEEN_PORTS;
- retryOnFailure=false;
- }
- /// How much time between each UDP send
- RakNet::Time TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL;
- RakNet::Time TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL;
- /// How many tries for one port before giving up and going to the next port
- int UDP_SENDS_PER_PORT_INTERNAL;
- int UDP_SENDS_PER_PORT_EXTERNAL;
- /// After giving up on one internal port, how long to wait before trying the next port
- int INTERNAL_IP_WAIT_AFTER_ATTEMPTS;
- /// How many external ports to try past the last known starting port
- int MAX_PREDICTIVE_PORT_RANGE;
- /// After sending TTL, how long to wait until first punch attempt
- int EXTERNAL_IP_WAIT_AFTER_FIRST_TTL;
- /// After giving up on one external port, how long to wait before trying the next port
- int EXTERNAL_IP_WAIT_BETWEEN_PORTS;
- /// After trying all external ports, how long to wait before returning ID_NAT_PUNCHTHROUGH_FAILED
- int EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS;
- /// Maximum number of internal IP address to try to connect to.
- /// Cannot be greater than MAXIMUM_NUMBER_OF_INTERNAL_IDS
- /// Should be high enough to try all internal IP addresses on the majority of computers
- int MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK;
- /// If the first punchthrough attempt fails, try again
- /// This sometimes works because the remote router was looking for an incoming message on a higher numbered port before responding to a lower numbered port from the other system
- bool retryOnFailure;
- };
- /// \ingroup NAT_PUNCHTHROUGH_GROUP
- struct RAK_DLL_EXPORT NatPunchthroughDebugInterface
- {
- NatPunchthroughDebugInterface() {}
- virtual ~NatPunchthroughDebugInterface() {}
- virtual void OnClientMessage(const char *msg)=0;
- };
- /// \ingroup NAT_PUNCHTHROUGH_GROUP
- struct RAK_DLL_EXPORT NatPunchthroughDebugInterface_Printf : public NatPunchthroughDebugInterface
- {
- virtual void OnClientMessage(const char *msg);
- };
- #if _RAKNET_SUPPORT_PacketLogger==1
- /// \ingroup NAT_PUNCHTHROUGH_GROUP
- struct RAK_DLL_EXPORT NatPunchthroughDebugInterface_PacketLogger : public NatPunchthroughDebugInterface
- {
- // Set to non-zero to write to the packetlogger!
- PacketLogger *pl;
- NatPunchthroughDebugInterface_PacketLogger() {pl=0;}
- ~NatPunchthroughDebugInterface_PacketLogger() {}
- virtual void OnClientMessage(const char *msg);
- };
- #endif
- /// \brief Client code for NATPunchthrough
- /// \details Maintain connection to NatPunchthroughServer to process incoming connection attempts through NatPunchthroughClient<BR>
- /// Client will send datagrams to port to estimate next port<BR>
- /// Will simultaneously connect with another client once ports are estimated.
- /// \sa NatTypeDetectionClient
- /// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html
- /// \ingroup NAT_PUNCHTHROUGH_GROUP
- class RAK_DLL_EXPORT NatPunchthroughClient : public PluginInterface2
- {
- public:
- // GetInstance() and DestroyInstance(instance*)
- STATIC_FACTORY_DECLARATIONS(NatPunchthroughClient)
- NatPunchthroughClient();
- ~NatPunchthroughClient();
- /// If the instance of RakPeer running NATPunchthroughServer was bound to two IP addresses, then you can call FindRouterPortStride()
- /// This will determine the stride that your router uses when assigning ports, if your router is full-cone
- /// This function is also called automatically when you call OpenNAT - however, calling it earlier when you are connected to the facilitator will speed up the process
- /// \param[in] destination The system to punch. Must already be connected to \a facilitator
- void FindRouterPortStride(const SystemAddress &facilitator);
- /// Punchthrough a NAT. Doesn't connect, just tries to setup the routing table
- /// \param[in] destination The system to punch. Must already be connected to \a facilitator
- /// \param[in] facilitator A system we are already connected to running the NatPunchthroughServer plugin
- /// \sa OpenNATGroup()
- /// You will get ID_NAT_PUNCHTHROUGH_SUCCEEDED on success
- /// You will get ID_NAT_TARGET_NOT_CONNECTED, ID_NAT_TARGET_UNRESPONSIVE, ID_NAT_CONNECTION_TO_TARGET_LOST, ID_NAT_ALREADY_IN_PROGRESS, or ID_NAT_PUNCHTHROUGH_FAILED on failures of various types
- /// However, if you lose connection to the facilitator, you may not necessarily get above
- bool OpenNAT(RakNetGUID destination, const SystemAddress &facilitator);
- /*
- /// \deprecated See FullyConnectedMesh2::StartVerifiedJoin() which is more flexible
- /// Same as calling OpenNAT for a list of systems, but reply is delayed until all systems pass.
- /// This is useful for peer to peer games where you want to connect to every system in the remote session, not just one particular system
- /// \note For cloud computing, all systems in the group must be connected to the same facilitator since we're only specifying one
- /// You will get ID_NAT_GROUP_PUNCH_SUCCEEDED on success
- /// You will get ID_NAT_TARGET_NOT_CONNECTED, ID_NAT_ALREADY_IN_PROGRESS, or ID_NAT_GROUP_PUNCH_FAILED on failures of various types
- /// However, if you lose connection to the facilitator, you may not necessarily get above
- bool OpenNATGroup(DataStructures::List<RakNetGUID> destinationSystems, const SystemAddress &facilitator);
- */
- /// Modify the system configuration if desired
- /// Don't modify the variables in the structure while punchthrough is in progress
- PunchthroughConfiguration* GetPunchthroughConfiguration(void);
- /// Sets a callback to be called with debug messages
- /// \param[in] i Pointer to an interface. The pointer is stored, so don't delete it while in progress. Pass 0 to clear.
- void SetDebugInterface(NatPunchthroughDebugInterface *i);
- /// Get the port mappings you should pass to UPNP (for miniupnpc-1.6.20120410, for the function UPNP_AddPortMapping)
- void GetUPNPPortMappings(char *externalPort, char *internalPort, const SystemAddress &natPunchthroughServerAddress);
- /// \internal For plugin handling
- virtual void Update(void);
- /// \internal For plugin handling
- virtual PluginReceiveResult OnReceive(Packet *packet);
- /// \internal For plugin handling
- virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming);
- /// \internal For plugin handling
- virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason );
- virtual void OnAttach(void);
- virtual void OnDetach(void);
- virtual void OnRakPeerShutdown(void);
- void Clear(void);
- struct SendPing
- {
- RakNet::Time nextActionTime;
- SystemAddress targetAddress;
- SystemAddress facilitator;
- SystemAddress internalIds[MAXIMUM_NUMBER_OF_INTERNAL_IDS];
- RakNetGUID targetGuid;
- bool weAreSender;
- int attemptCount;
- int retryCount;
- int punchingFixedPortAttempts; // only used for TestMode::PUNCHING_FIXED_PORT
- uint16_t sessionId;
- bool sentTTL;
- // Give priority to internal IP addresses because if we are on a LAN, we don't want to try to connect through the internet
- enum TestMode
- {
- TESTING_INTERNAL_IPS,
- WAITING_FOR_INTERNAL_IPS_RESPONSE,
- //SEND_WITH_TTL,
- TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_FACILITATOR_PORT,
- TESTING_EXTERNAL_IPS_1024_TO_FACILITATOR_PORT,
- TESTING_EXTERNAL_IPS_FACILITATOR_PORT_TO_1024,
- TESTING_EXTERNAL_IPS_1024_TO_1024,
- WAITING_AFTER_ALL_ATTEMPTS,
- // The trendnet remaps the remote port to 1024.
- // If you continue punching on a different port for the same IP it bans you and the communication becomes unidirectioal
- PUNCHING_FIXED_PORT,
- // try port 1024-1028
- } testMode;
- } sp;
- protected:
- unsigned short mostRecentExternalPort;
- //void OnNatGroupPunchthroughRequest(Packet *packet);
- void OnFailureNotification(Packet *packet);
- //void OnNatGroupPunchthroughReply(Packet *packet);
- void OnGetMostRecentPort(Packet *packet);
- void OnConnectAtTime(Packet *packet);
- unsigned int GetPendingOpenNATIndex(RakNetGUID destination, const SystemAddress &facilitator);
- void SendPunchthrough(RakNetGUID destination, const SystemAddress &facilitator);
- void QueueOpenNAT(RakNetGUID destination, const SystemAddress &facilitator);
- void SendQueuedOpenNAT(void);
- void SendTTL(const SystemAddress &sa);
- void SendOutOfBand(SystemAddress sa, MessageID oobId);
- void OnPunchthroughFailure(void);
- void OnReadyForNextPunchthrough(void);
- void PushFailure(void);
- bool RemoveFromFailureQueue(void);
- void PushSuccess(void);
- PunchthroughConfiguration pc;
- NatPunchthroughDebugInterface *natPunchthroughDebugInterface;
- // The first time we fail a NAT attempt, we add it to failedAttemptList and try again, since sometimes trying again later fixes the problem
- // The second time we fail, we return ID_NAT_PUNCHTHROUGH_FAILED
- struct AddrAndGuid
- {
- SystemAddress addr;
- RakNetGUID guid;
- };
- DataStructures::List<AddrAndGuid> failedAttemptList;
- struct DSTAndFac
- {
- RakNetGUID destination;
- SystemAddress facilitator;
- };
- DataStructures::Queue<DSTAndFac> queuedOpenNat;
- void IncrementExternalAttemptCount(RakNet::Time time, RakNet::Time delta);
- unsigned short portStride;
- enum
- {
- HAS_PORT_STRIDE,
- UNKNOWN_PORT_STRIDE,
- CALCULATING_PORT_STRIDE,
- INCAPABLE_PORT_STRIDE
- } hasPortStride;
- RakNet::Time portStrideCalTimeout;
- /*
- struct TimeAndGuid
- {
- RakNet::Time time;
- RakNetGUID guid;
- };
- DataStructures::List<TimeAndGuid> groupRequestsInProgress;
- struct GroupPunchRequest
- {
- SystemAddress facilitator;
- DataStructures::List<RakNetGUID> pendingList;
- DataStructures::List<RakNetGUID> passedListGuid;
- DataStructures::List<SystemAddress> passedListAddress;
- DataStructures::List<RakNetGUID> failedList;
- DataStructures::List<RakNetGUID> ignoredList;
- };
- DataStructures::List<GroupPunchRequest*> groupPunchRequests;
- void UpdateGroupPunchOnNatResult(SystemAddress facilitator, RakNetGUID targetSystem, SystemAddress targetSystemAddress, int result); // 0=failed, 1=success, 2=ignore
- */
- };
- } // namespace RakNet
- #endif
- #endif // _RAKNET_SUPPORT_*
|