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.
This commit is contained in:
Fabio Alessandrelli 2023-06-14 10:29:05 +02:00 committed by GitHub
commit 0aad5eb1cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 46 additions and 0 deletions

View file

@ -145,6 +145,17 @@
Call this function regularly to handle connections, disconnections, and to receive new packets. Call this function regularly to handle connections, disconnections, and to receive new packets.
</description> </description>
</method> </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> </methods>
<constants> <constants>
<constant name="COMPRESS_NONE" value="0" enum="CompressionMode"> <constant name="COMPRESS_NONE" value="0" enum="CompressionMode">

View file

@ -342,6 +342,39 @@ void ENetConnection::_broadcast(int p_channel, PackedByteArray p_packet, int p_f
broadcast(p_channel, pkt); 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() { 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_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)); 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_max_channels"), &ENetConnection::get_max_channels);
ClassDB::bind_method(D_METHOD("get_local_port"), &ENetConnection::get_local_port); 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("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_NONE);
BIND_ENUM_CONSTANT(COMPRESS_RANGE_CODER); BIND_ENUM_CONSTANT(COMPRESS_RANGE_CODER);

View file

@ -109,6 +109,7 @@ private:
public: public:
void broadcast(enet_uint8 p_channel, ENetPacket *p_packet); 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_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); 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(); void destroy();