Allow local port control on net_socket connections

This commit is contained in:
dam 2021-03-21 10:15:30 +00:00 committed by Fabio Alessandrelli
parent cee5414698
commit da8c2310b5
15 changed files with 163 additions and 37 deletions

View file

@ -67,6 +67,7 @@ public:
virtual bool is_open() const = 0;
virtual int get_available_bytes() const = 0;
virtual Error get_socket_address(IP_Address *r_ip, uint16_t *r_port) const = 0;
virtual Error set_broadcasting_enabled(bool p_enabled) = 0; // Returns OK if the socket option has been set successfully.
virtual void set_blocking_enabled(bool p_enabled) = 0;

View file

@ -163,6 +163,7 @@ Error PacketPeerUDP::listen(int p_port, const IP_Address &p_bind_address, int p_
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive).");
Error err;
IP::Type ip_type = IP::TYPE_ANY;
@ -210,6 +211,7 @@ Error PacketPeerUDP::connect_to_host(const IP_Address &p_host, int p_port) {
ERR_FAIL_COND_V(udp_server, ERR_LOCKED);
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(p_port < 1 || p_port > 65535, ERR_INVALID_PARAMETER, "The remote port number must be between 1 and 65535 (inclusive).");
Error err;
@ -328,6 +330,12 @@ int PacketPeerUDP::get_packet_port() const {
return packet_port;
}
int PacketPeerUDP::get_local_port() const {
uint16_t local_port;
_sock->get_socket_address(nullptr, &local_port);
return local_port;
}
void PacketPeerUDP::set_dest_address(const IP_Address &p_address, int p_port) {
ERR_FAIL_COND_MSG(connected, "Destination address cannot be set for connected sockets");
peer_addr = p_address;
@ -343,6 +351,7 @@ void PacketPeerUDP::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_connected_to_host"), &PacketPeerUDP::is_connected_to_host);
ClassDB::bind_method(D_METHOD("get_packet_ip"), &PacketPeerUDP::_get_packet_ip);
ClassDB::bind_method(D_METHOD("get_packet_port"), &PacketPeerUDP::get_packet_port);
ClassDB::bind_method(D_METHOD("get_local_port"), &PacketPeerUDP::get_local_port);
ClassDB::bind_method(D_METHOD("set_dest_address", "host", "port"), &PacketPeerUDP::_set_dest_address);
ClassDB::bind_method(D_METHOD("set_broadcast_enabled", "enabled"), &PacketPeerUDP::set_broadcast_enabled);
ClassDB::bind_method(D_METHOD("join_multicast_group", "multicast_address", "interface_name"), &PacketPeerUDP::join_multicast_group);

View file

@ -83,6 +83,7 @@ public:
IP_Address get_packet_address() const;
int get_packet_port() const;
int get_local_port() const;
void set_dest_address(const IP_Address &p_address, int p_port);
Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;

View file

@ -67,21 +67,40 @@ void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint
peer_port = p_port;
}
Error StreamPeerTCP::connect_to_host(const IP_Address &p_host, uint16_t p_port) {
Error StreamPeerTCP::bind(int p_port, const IP_Address &p_host) {
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive).");
Error err;
IP::Type ip_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
err = _sock->open(NetSocket::TYPE_TCP, ip_type);
ERR_FAIL_COND_V(err != OK, FAILED);
if (p_host.is_wildcard()) {
ip_type = IP::TYPE_ANY;
}
Error err = _sock->open(NetSocket::TYPE_TCP, ip_type);
if (err != OK) {
return err;
}
_sock->set_blocking_enabled(false);
return _sock->bind(p_host, p_port);
}
Error StreamPeerTCP::connect_to_host(const IP_Address &p_host, int p_port) {
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(status != STATUS_NONE, ERR_ALREADY_IN_USE);
ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(p_port < 1 || p_port > 65535, ERR_INVALID_PARAMETER, "The remote port number must be between 1 and 65535 (inclusive).");
if (!_sock->is_open()) {
IP::Type ip_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
Error err = _sock->open(NetSocket::TYPE_TCP, ip_type);
if (err != OK) {
return err;
}
_sock->set_blocking_enabled(false);
}
timeout = OS::get_singleton()->get_ticks_msec() + (((uint64_t)GLOBAL_GET("network/limits/tcp/connect_timeout_seconds")) * 1000);
err = _sock->connect_to_host(p_host, p_port);
Error err = _sock->connect_to_host(p_host, p_port);
if (err == OK) {
status = STATUS_CONNECTED;
@ -300,10 +319,16 @@ IP_Address StreamPeerTCP::get_connected_host() const {
return peer_host;
}
uint16_t StreamPeerTCP::get_connected_port() const {
int StreamPeerTCP::get_connected_port() const {
return peer_port;
}
int StreamPeerTCP::get_local_port() const {
uint16_t local_port;
_sock->get_socket_address(nullptr, &local_port);
return local_port;
}
Error StreamPeerTCP::_connect(const String &p_address, int p_port) {
IP_Address ip;
if (p_address.is_valid_ip_address()) {
@ -319,11 +344,13 @@ Error StreamPeerTCP::_connect(const String &p_address, int p_port) {
}
void StreamPeerTCP::_bind_methods() {
ClassDB::bind_method(D_METHOD("bind", "port", "host"), &StreamPeerTCP::bind, DEFVAL("*"));
ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port"), &StreamPeerTCP::_connect);
ClassDB::bind_method(D_METHOD("is_connected_to_host"), &StreamPeerTCP::is_connected_to_host);
ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerTCP::get_status);
ClassDB::bind_method(D_METHOD("get_connected_host"), &StreamPeerTCP::get_connected_host);
ClassDB::bind_method(D_METHOD("get_connected_port"), &StreamPeerTCP::get_connected_port);
ClassDB::bind_method(D_METHOD("get_local_port"), &StreamPeerTCP::get_local_port);
ClassDB::bind_method(D_METHOD("disconnect_from_host"), &StreamPeerTCP::disconnect_from_host);
ClassDB::bind_method(D_METHOD("set_no_delay", "enabled"), &StreamPeerTCP::set_no_delay);

View file

@ -65,10 +65,12 @@ protected:
public:
void accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint16_t p_port);
Error connect_to_host(const IP_Address &p_host, uint16_t p_port);
Error bind(int p_port, const IP_Address &p_host);
Error connect_to_host(const IP_Address &p_host, int p_port);
bool is_connected_to_host() const;
IP_Address get_connected_host() const;
uint16_t get_connected_port() const;
int get_connected_port() const;
int get_local_port() const;
void disconnect_from_host();
int get_available_bytes() const override;

View file

@ -34,6 +34,7 @@ void TCP_Server::_bind_methods() {
ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &TCP_Server::listen, DEFVAL("*"));
ClassDB::bind_method(D_METHOD("is_connection_available"), &TCP_Server::is_connection_available);
ClassDB::bind_method(D_METHOD("is_listening"), &TCP_Server::is_listening);
ClassDB::bind_method(D_METHOD("get_local_port"), &TCP_Server::get_local_port);
ClassDB::bind_method(D_METHOD("take_connection"), &TCP_Server::take_connection);
ClassDB::bind_method(D_METHOD("stop"), &TCP_Server::stop);
}
@ -42,6 +43,7 @@ Error TCP_Server::listen(uint16_t p_port, const IP_Address &p_bind_address) {
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive).");
Error err;
IP::Type ip_type = IP::TYPE_ANY;
@ -74,6 +76,12 @@ Error TCP_Server::listen(uint16_t p_port, const IP_Address &p_bind_address) {
return OK;
}
int TCP_Server::get_local_port() const {
uint16_t local_port;
_sock->get_socket_address(nullptr, &local_port);
return local_port;
}
bool TCP_Server::is_listening() const {
ERR_FAIL_COND_V(!_sock.is_valid(), false);

View file

@ -49,6 +49,7 @@ protected:
public:
Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*"));
int get_local_port() const;
bool is_listening() const;
bool is_connection_available() const;
Ref<StreamPeerTCP> take_connection();

View file

@ -34,6 +34,7 @@ void UDPServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &UDPServer::listen, DEFVAL("*"));
ClassDB::bind_method(D_METHOD("poll"), &UDPServer::poll);
ClassDB::bind_method(D_METHOD("is_connection_available"), &UDPServer::is_connection_available);
ClassDB::bind_method(D_METHOD("get_local_port"), &UDPServer::get_local_port);
ClassDB::bind_method(D_METHOD("is_listening"), &UDPServer::is_listening);
ClassDB::bind_method(D_METHOD("take_connection"), &UDPServer::take_connection);
ClassDB::bind_method(D_METHOD("stop"), &UDPServer::stop);
@ -90,6 +91,7 @@ Error UDPServer::listen(uint16_t p_port, const IP_Address &p_bind_address) {
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive).");
Error err;
IP::Type ip_type = IP::TYPE_ANY;
@ -112,11 +114,15 @@ Error UDPServer::listen(uint16_t p_port, const IP_Address &p_bind_address) {
stop();
return err;
}
bind_address = p_bind_address;
bind_port = p_port;
return OK;
}
int UDPServer::get_local_port() const {
uint16_t local_port;
_sock->get_socket_address(nullptr, &local_port);
return local_port;
}
bool UDPServer::is_listening() const {
ERR_FAIL_COND_V(!_sock.is_valid(), false);
@ -176,8 +182,6 @@ void UDPServer::stop() {
if (_sock.is_valid()) {
_sock->close();
}
bind_port = 0;
bind_address = IP_Address();
List<Peer>::Element *E = peers.front();
while (E) {
E->get().peer->disconnect_shared_socket();

View file

@ -53,21 +53,18 @@ protected:
};
uint8_t recv_buffer[PACKET_BUFFER_SIZE];
int bind_port = 0;
IP_Address bind_address;
List<Peer> peers;
List<Peer> pending;
int max_pending_connections = 16;
Ref<NetSocket> _sock;
static void _bind_methods();
public:
void remove_peer(IP_Address p_ip, int p_port);
Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*"));
Error poll();
int get_local_port() const;
bool is_listening() const;
bool is_connection_available() const;
void set_max_pending_connections(int p_max);

View file

@ -28,6 +28,13 @@
[b]Note:[/b] Connecting to the remote peer does not help to protect from malicious attacks like IP spoofing, etc. Think about using an encryption technique like SSL or DTLS if you feel like your application is transferring sensitive information.
</description>
</method>
<method name="get_local_port" qualifiers="const">
<return type="int">
</return>
<description>
Returns the local port to which this peer is bound.
</description>
</method>
<method name="get_packet_ip" qualifiers="const">
<return type="String">
</return>

View file

@ -9,6 +9,18 @@
<tutorials>
</tutorials>
<methods>
<method name="bind">
<return type="int" enum="Error">
</return>
<argument index="0" name="port" type="int">
</argument>
<argument index="1" name="host" type="String" default="&quot;*&quot;">
</argument>
<description>
Opens the TCP socket, and binds it to the specified local address.
This method is generally not needed, and only used to force the subsequent call to [method connect_to_host] to use the specified [code]host[/code] and [code]port[/code] as source address. This can be desired in some NAT punchthrough techniques, or when forcing the source network interface.
</description>
</method>
<method name="connect_to_host">
<return type="int" enum="Error">
</return>
@ -17,7 +29,7 @@
<argument index="1" name="port" type="int">
</argument>
<description>
Connects to the specified [code]host:port[/code] pair. A hostname will be resolved if valid. Returns [constant OK] on success or [constant FAILED] on failure.
Connects to the specified [code]host:port[/code] pair. A hostname will be resolved if valid. Returns [constant OK] on success.
</description>
</method>
<method name="disconnect_from_host">
@ -41,6 +53,13 @@
Returns the port of this peer.
</description>
</method>
<method name="get_local_port" qualifiers="const">
<return type="int">
</return>
<description>
Returns the local port to which this peer is bound.
</description>
</method>
<method name="get_status">
<return type="int" enum="StreamPeerTCP.Status">
</return>

View file

@ -9,6 +9,13 @@
<tutorials>
</tutorials>
<methods>
<method name="get_local_port" qualifiers="const">
<return type="int">
</return>
<description>
Returns the local port this server is listening to.
</description>
</method>
<method name="is_connection_available" qualifiers="const">
<return type="bool">
</return>

View file

@ -123,6 +123,13 @@
<tutorials>
</tutorials>
<methods>
<method name="get_local_port" qualifiers="const">
<return type="int">
</return>
<description>
Returns the local port this server is listening to.
</description>
</method>
<method name="is_connection_available" qualifiers="const">
<return type="bool">
</return>

View file

@ -130,18 +130,23 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const
}
}
void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IP_Address &r_ip, uint16_t &r_port) {
void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IP_Address *r_ip, uint16_t *r_port) {
if (p_addr->ss_family == AF_INET) {
struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr;
r_ip.set_ipv4((uint8_t *)&(addr4->sin_addr.s_addr));
r_port = ntohs(addr4->sin_port);
if (r_ip) {
r_ip->set_ipv4((uint8_t *)&(addr4->sin_addr.s_addr));
}
if (r_port) {
*r_port = ntohs(addr4->sin_port);
}
} else if (p_addr->ss_family == AF_INET6) {
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr;
r_ip.set_ipv6(addr6->sin6_addr.s6_addr);
r_port = ntohs(addr6->sin6_port);
if (r_ip) {
r_ip->set_ipv6(addr6->sin6_addr.s6_addr);
}
if (r_port) {
*r_port = ntohs(addr6->sin6_port);
}
};
}
@ -186,13 +191,21 @@ NetSocketPosix::~NetSocketPosix() {
NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
#if defined(WINDOWS_ENABLED)
int err = WSAGetLastError();
if (err == WSAEISCONN)
if (err == WSAEISCONN) {
return ERR_NET_IS_CONNECTED;
if (err == WSAEINPROGRESS || err == WSAEALREADY)
}
if (err == WSAEINPROGRESS || err == WSAEALREADY) {
return ERR_NET_IN_PROGRESS;
if (err == WSAEWOULDBLOCK)
}
if (err == WSAEWOULDBLOCK) {
return ERR_NET_WOULD_BLOCK;
}
if (err == WSAEADDRINUSE || err == WSAEADDRNOTAVAIL) {
return ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE;
}
if (err == WSAEACCES) {
return ERR_NET_UNAUTHORIZED;
}
print_verbose("Socket error: " + itos(err));
return ERR_NET_OTHER;
#else
@ -205,6 +218,12 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return ERR_NET_WOULD_BLOCK;
}
if (errno == EADDRINUSE || errno == EINVAL || errno == EADDRNOTAVAIL) {
return ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE;
}
if (errno == EACCES) {
return ERR_NET_UNAUTHORIZED;
}
print_verbose("Socket error: " + itos(errno));
return ERR_NET_OTHER;
#endif
@ -384,8 +403,8 @@ Error NetSocketPosix::bind(IP_Address p_addr, uint16_t p_port) {
size_t addr_size = _set_addr_storage(&addr, p_addr, p_port, _ip_type);
if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
_get_socket_error();
print_verbose("Failed to bind socket.");
NetError err = _get_socket_error();
print_verbose("Failed to bind socket. Error: " + itos(err));
close();
return ERR_UNAVAILABLE;
}
@ -716,6 +735,20 @@ int NetSocketPosix::get_available_bytes() const {
return len;
}
Error NetSocketPosix::get_socket_address(IP_Address *r_ip, uint16_t *r_port) const {
ERR_FAIL_COND_V(!is_open(), FAILED);
struct sockaddr_storage saddr;
socklen_t len = sizeof(saddr);
if (getsockname(_sock, (struct sockaddr *)&saddr, &len) != 0) {
_get_socket_error();
print_verbose("Error when reading local socket address.");
return FAILED;
}
_set_ip_port(&saddr, r_ip, r_port);
return OK;
}
Ref<NetSocket> NetSocketPosix::accept(IP_Address &r_ip, uint16_t &r_port) {
Ref<NetSocket> out;
ERR_FAIL_COND_V(!is_open(), out);
@ -729,7 +762,7 @@ Ref<NetSocket> NetSocketPosix::accept(IP_Address &r_ip, uint16_t &r_port) {
return out;
}
_set_ip_port(&their_addr, r_ip, r_port);
_set_ip_port(&their_addr, &r_ip, &r_port);
NetSocketPosix *ns = memnew(NetSocketPosix);
ns->_set_socket(fd, _ip_type, _is_stream);

View file

@ -54,7 +54,9 @@ private:
ERR_NET_WOULD_BLOCK,
ERR_NET_IS_CONNECTED,
ERR_NET_IN_PROGRESS,
ERR_NET_OTHER
ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE,
ERR_NET_UNAUTHORIZED,
ERR_NET_OTHER,
};
NetError _get_socket_error() const;
@ -70,7 +72,7 @@ protected:
public:
static void make_default();
static void cleanup();
static void _set_ip_port(struct sockaddr_storage *p_addr, IP_Address &r_ip, uint16_t &r_port);
static void _set_ip_port(struct sockaddr_storage *p_addr, IP_Address *r_ip, uint16_t *r_port);
static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type);
virtual Error open(Type p_sock_type, IP::Type &ip_type);
@ -87,6 +89,7 @@ public:
virtual bool is_open() const;
virtual int get_available_bytes() const;
virtual Error get_socket_address(IP_Address *r_ip, uint16_t *r_port) const;
virtual Error set_broadcasting_enabled(bool p_enabled);
virtual void set_blocking_enabled(bool p_enabled);