Browse Source

Merge pull request #77627 from TestSubject06/reciprocal-conns

Allow an ENetConnection to send a packet to an arbitrary destination for the purposes of establishing NAT routing table entries.
Fabio Alessandrelli 2 years ago
parent
commit
0aad5eb1cf

+ 11 - 0
modules/enet/doc_classes/ENetConnection.xml

@@ -145,6 +145,17 @@
 				Call this function regularly to handle connections, disconnections, and to receive new packets.
 			</description>
 		</method>
+		<method name="socket_send">
+			<return type="void" />
+			<param index="0" name="destination_address" type="String" />
+			<param index="1" name="destination_port" type="int" />
+			<param index="2" name="packet" type="PackedByteArray" />
+			<description>
+				Sends a [param packet] toward a destination from the address and port currently bound by this ENetConnection instance. 
+				This is useful as it serves to establish entries in NAT routing tables on all devices between this bound instance and the public facing internet, allowing a prospective client's connection packets to be routed backward through the NAT device(s) between the public internet and this host.
+				This requires forward knowledge of a prospective client's address and communication port as seen by the public internet - after any NAT devices have handled their connection request. This information can be obtained by a [url=https://en.wikipedia.org/wiki/STUN]STUN[/url] service, and must be handed off to your host by an entity that is not the prospective client. This will never work for a client behind a Symmetric NAT due to the nature of the Symmetric NAT routing algorithm, as their IP and Port cannot be known beforehand.
+			</description>
+		</method>
 	</methods>
 	<constants>
 		<constant name="COMPRESS_NONE" value="0" enum="CompressionMode">

+ 34 - 0
modules/enet/enet_connection.cpp

@@ -342,6 +342,39 @@ void ENetConnection::_broadcast(int p_channel, PackedByteArray p_packet, int p_f
 	broadcast(p_channel, pkt);
 }
 
+void ENetConnection::socket_send(const String &p_address, int p_port, const PackedByteArray &p_packet) {
+	ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active.");
+	ERR_FAIL_COND_MSG(!(host->socket), "The ENetConnection instance isn't currently bound");
+	ERR_FAIL_COND_MSG(p_port < 1 || p_port > 65535, "The remote port number must be between 1 and 65535 (inclusive).");
+
+	IPAddress ip;
+	if (p_address.is_valid_ip_address()) {
+		ip = p_address;
+	} else {
+#ifdef GODOT_ENET
+		ip = IP::get_singleton()->resolve_hostname(p_address);
+#else
+		ip = IP::get_singleton()->resolve_hostname(p_address, IP::TYPE_IPV4);
+#endif
+		ERR_FAIL_COND_MSG(!ip.is_valid(), "Couldn't resolve the server IP address or domain name.");
+	}
+
+	ENetAddress address;
+#ifdef GODOT_ENET
+	enet_address_set_ip(&address, ip.get_ipv6(), 16);
+#else
+	ERR_FAIL_COND_MSG(!ip.is_ipv4(), "Connecting to an IPv6 server isn't supported when using vanilla ENet. Recompile Godot with the bundled ENet library.");
+	address.host = *(uint32_t *)ip.get_ipv4();
+#endif
+	address.port = p_port;
+
+	ENetBuffer enet_buffers[1];
+	enet_buffers[0].data = (void *)p_packet.ptr();
+	enet_buffers[0].dataLength = p_packet.size();
+
+	enet_socket_send(host->socket, &address, enet_buffers, 1);
+}
+
 void ENetConnection::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("create_host_bound", "bind_address", "bind_port", "max_peers", "max_channels", "in_bandwidth", "out_bandwidth"), &ENetConnection::create_host_bound, DEFVAL(32), DEFVAL(0), DEFVAL(0), DEFVAL(0));
 	ClassDB::bind_method(D_METHOD("create_host", "max_peers", "max_channels", "in_bandwidth", "out_bandwidth"), &ENetConnection::create_host, DEFVAL(32), DEFVAL(0), DEFVAL(0), DEFVAL(0));
@@ -360,6 +393,7 @@ void ENetConnection::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_max_channels"), &ENetConnection::get_max_channels);
 	ClassDB::bind_method(D_METHOD("get_local_port"), &ENetConnection::get_local_port);
 	ClassDB::bind_method(D_METHOD("get_peers"), &ENetConnection::_get_peers);
+	ClassDB::bind_method(D_METHOD("socket_send", "destination_address", "destination_port", "packet"), &ENetConnection::socket_send);
 
 	BIND_ENUM_CONSTANT(COMPRESS_NONE);
 	BIND_ENUM_CONSTANT(COMPRESS_RANGE_CODER);

+ 1 - 0
modules/enet/enet_connection.h

@@ -109,6 +109,7 @@ private:
 
 public:
 	void broadcast(enet_uint8 p_channel, ENetPacket *p_packet);
+	void socket_send(const String &p_address, int p_port, const PackedByteArray &p_packet);
 	Error create_host_bound(const IPAddress &p_bind_address = IPAddress("*"), int p_port = 0, int p_max_peers = 32, int p_max_channels = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0);
 	Error create_host(int p_max_peers = 32, int p_max_channels = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0);
 	void destroy();