1 
2 /// \file
3 /// \brief Contains the NAT-punchthrough plugin for the client.
4 ///
5 /// This file is part of RakNet Copyright 2003 Jenkins Software LLC
6 ///
7 /// Raknet is available under the terms of the GPLv3 license, see /usr/local/share/licenses/raknet-3.9.2_10,1/GPLv3.
8 
9 #include "NativeFeatureIncludes.h"
10 #if _RAKNET_SUPPORT_NatPunchthroughClient==1
11 
12 #ifndef __NAT_PUNCHTHROUGH_CLIENT_H
13 #define __NAT_PUNCHTHROUGH_CLIENT_H
14 
15 #include "RakNetTypes.h"
16 #include "Export.h"
17 #include "PluginInterface2.h"
18 #include "PacketPriority.h"
19 #include "SocketIncludes.h"
20 #include "DS_List.h"
21 #include "RakString.h"
22 
23 // Trendnet TEW-632BRP sometimes starts at port 1024 and increments sequentially.
24 // Zonnet zsr1134we. Replies go out on the net, but are always absorbed by the remote router??
25 // Dlink ebr2310 to Trendnet ok
26 // Trendnet TEW-652BRP to Trendnet 632BRP OK
27 // Trendnet TEW-632BRP to Trendnet 632BRP OK
28 // Buffalo WHR-HP-G54 OK
29 // Netgear WGR614 ok
30 
31 class RakPeerInterface;
32 struct Packet;
33 #if _RAKNET_SUPPORT_PacketLogger==1
34 class PacketLogger;
35 #endif
36 
37 /// \ingroup NAT_PUNCHTHROUGH_GROUP
38 struct RAK_DLL_EXPORT PunchthroughConfiguration
39 {
40 	/// internal: (15 ms * 2 tries + 30 wait) * 5 ports * 8 players = 2.4 seconds
41 	/// external: (50 ms * 8 sends + 100 wait) * 2 port * 8 players = 8 seconds
42 	/// Total: 8 seconds
PunchthroughConfigurationPunchthroughConfiguration43 	PunchthroughConfiguration() {
44 		TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL=15;
45 		TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL=50;
46 		UDP_SENDS_PER_PORT_INTERNAL=2;
47 		UDP_SENDS_PER_PORT_EXTERNAL=8;
48 		INTERNAL_IP_WAIT_AFTER_ATTEMPTS=30;
49 		MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK=5; /// set to 0 to not do lan connects
50 		MAX_PREDICTIVE_PORT_RANGE=2;
51 		EXTERNAL_IP_WAIT_BETWEEN_PORTS=100;
52 		EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS=EXTERNAL_IP_WAIT_BETWEEN_PORTS;
53 		retryOnFailure=false;
54 	}
55 
56 	/// How much time between each UDP send
57 	RakNetTimeMS TIME_BETWEEN_PUNCH_ATTEMPTS_INTERNAL;
58 	RakNetTimeMS TIME_BETWEEN_PUNCH_ATTEMPTS_EXTERNAL;
59 
60 	/// How many tries for one port before giving up and going to the next port
61 	int UDP_SENDS_PER_PORT_INTERNAL;
62 	int UDP_SENDS_PER_PORT_EXTERNAL;
63 
64 	/// After giving up on one internal port, how long to wait before trying the next port
65 	int INTERNAL_IP_WAIT_AFTER_ATTEMPTS;
66 
67 	/// How many external ports to try past the last known starting port
68 	int MAX_PREDICTIVE_PORT_RANGE;
69 
70 	/// After giving up on one external  port, how long to wait before trying the next port
71 	int EXTERNAL_IP_WAIT_BETWEEN_PORTS;
72 
73 	/// After trying all external ports, how long to wait before returning ID_NAT_PUNCHTHROUGH_FAILED
74 	int EXTERNAL_IP_WAIT_AFTER_ALL_ATTEMPTS;
75 
76 	/// Maximum number of internal IP address to try to connect to.
77 	/// Cannot be greater than MAXIMUM_NUMBER_OF_INTERNAL_IDS
78 	/// Should be high enough to try all internal IP addresses on the majority of computers
79 	int MAXIMUM_NUMBER_OF_INTERNAL_IDS_TO_CHECK;
80 
81 	/// If the first punchthrough attempt fails, try again
82 	/// 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
83 	bool retryOnFailure;
84 };
85 
86 /// \ingroup NAT_PUNCHTHROUGH_GROUP
87 struct RAK_DLL_EXPORT NatPunchthroughDebugInterface
88 {
NatPunchthroughDebugInterfaceNatPunchthroughDebugInterface89 	NatPunchthroughDebugInterface() {}
~NatPunchthroughDebugInterfaceNatPunchthroughDebugInterface90 	virtual ~NatPunchthroughDebugInterface() {}
91 	virtual void OnClientMessage(const char *msg)=0;
92 };
93 
94 /// \ingroup NAT_PUNCHTHROUGH_GROUP
95 struct RAK_DLL_EXPORT NatPunchthroughDebugInterface_Printf : public NatPunchthroughDebugInterface
96 {
97 	virtual void OnClientMessage(const char *msg);
98 };
99 
100 #if _RAKNET_SUPPORT_PacketLogger==1
101 /// \ingroup NAT_PUNCHTHROUGH_GROUP
102 struct RAK_DLL_EXPORT NatPunchthroughDebugInterface_PacketLogger : public NatPunchthroughDebugInterface
103 {
104 	// Set to non-zero to write to the packetlogger!
105 	PacketLogger *pl;
106 
NatPunchthroughDebugInterface_PacketLoggerNatPunchthroughDebugInterface_PacketLogger107 	NatPunchthroughDebugInterface_PacketLogger() {pl=0;}
~NatPunchthroughDebugInterface_PacketLoggerNatPunchthroughDebugInterface_PacketLogger108 	~NatPunchthroughDebugInterface_PacketLogger() {}
109 	virtual void OnClientMessage(const char *msg);
110 };
111 #endif
112 
113 /// \brief Client code for NATPunchthrough
114 /// \details Maintain connection to NatPunchthroughServer to process incoming connection attempts through NatPunchthroughClient<BR>
115 /// Client will send datagrams to port to estimate next port<BR>
116 /// Will simultaneously connect with another client once ports are estimated.
117 /// \sa NatTypeDetectionClient
118 /// See also http://www.jenkinssoftware.com/raknet/manual/natpunchthrough.html
119 /// \ingroup NAT_PUNCHTHROUGH_GROUP
120 class RAK_DLL_EXPORT NatPunchthroughClient : public PluginInterface2
121 {
122 public:
123 	NatPunchthroughClient();
124 	~NatPunchthroughClient();
125 
126 	/// Punchthrough a NAT. Doesn't connect, just tries to setup the routing table
127 	bool OpenNAT(RakNetGUID destination, SystemAddress facilitator);
128 
129 	/// Modify the system configuration if desired
130 	/// Don't modify the variables in the structure while punchthrough is in progress
131 	PunchthroughConfiguration* GetPunchthroughConfiguration(void);
132 
133 	/// Sets a callback to be called with debug messages
134 	/// \param[in] i Pointer to an interface. The pointer is stored, so don't delete it while in progress. Pass 0 to clear.
135 	void SetDebugInterface(NatPunchthroughDebugInterface *i);
136 
137 	/// Returns the port on the router that incoming messages will be sent to
138 	/// UPNP needs to know this (See UPNP project)
139 	/// \pre Must have connected to the facilitator first
140 	/// \return Port that incoming messages will be sent to, from other clients. This probably won't be the same port RakNet was started on.
141 	unsigned short GetUPNPExternalPort(void) const;
142 
143 	/// Returns our internal port that RakNet was started on
144 	/// Equivalent to calling rakPeer->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).port
145 	/// \return Port that incoming messages will arrive on, on our actual system.
146 	unsigned short GetUPNPInternalPort(void) const;
147 
148 	/// Returns our locally bound system address
149 	/// Equivalent to calling rakPeer->GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).ToString(false);
150 	/// \return Internal address that UPNP should forward messages to
151 	RakNet::RakString GetUPNPInternalAddress(void) const;
152 
153 	/// \internal For plugin handling
154 	virtual void Update(void);
155 
156 	/// \internal For plugin handling
157 	virtual PluginReceiveResult OnReceive(Packet *packet);
158 
159 	/// \internal For plugin handling
160 	virtual void OnNewConnection(SystemAddress systemAddress, RakNetGUID rakNetGUID, bool isIncoming);
161 
162 	/// \internal For plugin handling
163 	virtual void OnClosedConnection(SystemAddress systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason );
164 
165 	virtual void OnAttach(void);
166 	virtual void OnDetach(void);
167 	virtual void OnRakPeerShutdown(void);
168 	void Clear(void);
169 
170 protected:
171 	unsigned short mostRecentNewExternalPort;
172 	void OnGetMostRecentPort(Packet *packet);
173 	void OnConnectAtTime(Packet *packet);
174 	unsigned int GetPendingOpenNATIndex(RakNetGUID destination, SystemAddress facilitator);
175 	void SendPunchthrough(RakNetGUID destination, SystemAddress facilitator);
176 	void SendTTL(SystemAddress sa);
177 	void SendOutOfBand(SystemAddress sa, MessageID oobId);
178 	void OnPunchthroughFailure(void);
179 	void OnReadyForNextPunchthrough(void);
180 	void PushFailure(void);
181 	bool RemoveFromFailureQueue(void);
182 	void PushSuccess(void);
183 	//void ProcessNextPunchthroughQueue(void);
184 
185 	/*
186 	struct PendingOpenNAT
187 	{
188 		RakNetGUID destination;
189 		SystemAddress facilitator;
190 	};
191 	DataStructures::List<PendingOpenNAT> pendingOpenNAT;
192 	*/
193 
194 	struct SendPing
195 	{
196 		RakNetTime nextActionTime;
197 		SystemAddress targetAddress;
198 		SystemAddress facilitator;
199 		SystemAddress internalIds[MAXIMUM_NUMBER_OF_INTERNAL_IDS];
200 		RakNetGUID targetGuid;
201 		bool weAreSender;
202 		int attemptCount;
203 		int retryCount;
204 		int punchingFixedPortAttempts; // only used for TestMode::PUNCHING_FIXED_PORT
205 		uint16_t sessionId;
206 		// Give priority to internal IP addresses because if we are on a LAN, we don't want to try to connect through the internet
207 		enum TestMode
208 		{
209 			TESTING_INTERNAL_IPS,
210 			WAITING_FOR_INTERNAL_IPS_RESPONSE,
211 			TESTING_EXTERNAL_IPS_FROM_FACILITATOR_PORT,
212 			TESTING_EXTERNAL_IPS_FROM_1024,
213 			WAITING_AFTER_ALL_ATTEMPTS,
214 
215 			// The trendnet remaps the remote port to 1024.
216 			// If you continue punching on a different port for the same IP it bans you and the communication becomes unidirectioal
217 			PUNCHING_FIXED_PORT,
218 
219 			// try port 1024-1028
220 		} testMode;
221 	} sp;
222 
223 	PunchthroughConfiguration pc;
224 	NatPunchthroughDebugInterface *natPunchthroughDebugInterface;
225 
226 	// The first time we fail a NAT attempt, we add it to failedAttemptList and try again, since sometimes trying again later fixes the problem
227 	// The second time we fail, we return ID_NAT_PUNCHTHROUGH_FAILED
228 	struct AddrAndGuid
229 	{
230 		SystemAddress addr;
231 		RakNetGUID guid;
232 	};
233 	DataStructures::List<AddrAndGuid> failedAttemptList;
234 };
235 
236 #endif
237 
238 #endif // _RAKNET_SUPPORT_*
239