From 0c620b29cdea566577e105b49a2273db7a315675 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Fri, 8 Nov 2024 13:34:14 +0100 Subject: [PATCH] [Net] Split Unix/Windows NetSocket implementation --- core/io/net_socket.h | 2 + ...t_socket_posix.cpp => net_socket_unix.cpp} | 262 ++------ .../{net_socket_posix.h => net_socket_unix.h} | 70 +- drivers/unix/os_unix.cpp | 2 +- drivers/windows/net_socket_winsock.cpp | 613 ++++++++++++++++++ drivers/windows/net_socket_winsock.h | 102 +++ platform/android/net_socket_android.h | 2 +- platform/windows/os_windows.cpp | 6 +- 8 files changed, 801 insertions(+), 258 deletions(-) rename drivers/unix/{net_socket_posix.cpp => net_socket_unix.cpp} (70%) rename drivers/unix/{net_socket_posix.h => net_socket_unix.h} (72%) create mode 100644 drivers/windows/net_socket_winsock.cpp create mode 100644 drivers/windows/net_socket_winsock.h diff --git a/core/io/net_socket.h b/core/io/net_socket.h index 120ad5e85b3..c12bab622af 100644 --- a/core/io/net_socket.h +++ b/core/io/net_socket.h @@ -76,6 +76,8 @@ public: virtual void set_reuse_address_enabled(bool p_enabled) = 0; virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) = 0; virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) = 0; + + virtual ~NetSocket() {} }; #endif // NET_SOCKET_H diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_unix.cpp similarity index 70% rename from drivers/unix/net_socket_posix.cpp rename to drivers/unix/net_socket_unix.cpp index 5caa33100ee..3be615d9ad6 100644 --- a/drivers/unix/net_socket_posix.cpp +++ b/drivers/unix/net_socket_unix.cpp @@ -1,5 +1,5 @@ /**************************************************************************/ -/* net_socket_posix.cpp */ +/* net_socket_unix.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,13 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "net_socket_posix.h" - // Some proprietary Unix-derived platforms don't expose Unix sockets // so this allows skipping this file to reimplement this API differently. -#ifndef UNIX_SOCKET_UNAVAILABLE +#if defined(UNIX_ENABLED) && !defined(UNIX_SOCKET_UNAVAILABLE) -#if defined(UNIX_ENABLED) +#include "net_socket_unix.h" #include #include @@ -62,44 +60,11 @@ #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP #endif -// Some custom defines to minimize ifdefs -#define SOCK_EMPTY -1 -#define SOCK_BUF(x) x -#define SOCK_CBUF(x) x -#define SOCK_IOCTL ioctl -#define SOCK_FIONREAD_LEN_TYPE int -#define SOCK_CLOSE ::close -#define SOCK_CONNECT(p_sock, p_addr, p_addr_len) ::connect(p_sock, p_addr, p_addr_len) - -/* Windows */ -#elif defined(WINDOWS_ENABLED) -#include -#include - -#include -// Some custom defines to minimize ifdefs -#define SOCK_EMPTY INVALID_SOCKET -#define SOCK_BUF(x) (char *)(x) -#define SOCK_CBUF(x) (const char *)(x) -#define SOCK_IOCTL ioctlsocket -#define SOCK_FIONREAD_LEN_TYPE unsigned long -#define SOCK_CLOSE closesocket -// connect is broken on windows under certain conditions, reasons unknown: -// See https://github.com/godotengine/webrtc-native/issues/6 -#define SOCK_CONNECT(p_sock, p_addr, p_addr_len) ::WSAConnect(p_sock, p_addr, p_addr_len, nullptr, nullptr, nullptr, nullptr) - -// Workaround missing flag in MinGW -#if defined(__MINGW32__) && !defined(SIO_UDP_NETRESET) -#define SIO_UDP_NETRESET _WSAIOW(IOC_VENDOR, 15) -#endif - -#endif // UNIX_ENABLED - size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) { memset(p_addr, 0, sizeof(struct sockaddr_storage)); - if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket + if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket. - // IPv6 only socket with IPv4 address + // IPv6 only socket with IPv4 address. ERR_FAIL_COND_V(!p_ip.is_wildcard() && p_ip_type == IP::TYPE_IPV6 && p_ip.is_ipv4(), 0); struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; @@ -111,14 +76,14 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const addr6->sin6_addr = in6addr_any; } return sizeof(sockaddr_in6); - } else { // IPv4 socket + } else { // IPv4 socket. - // IPv4 socket with IPv6 address + // IPv4 socket with IPv6 address. ERR_FAIL_COND_V(!p_ip.is_wildcard() && !p_ip.is_ipv4(), 0); struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; addr4->sin_family = AF_INET; - addr4->sin_port = htons(p_port); // short, network byte order + addr4->sin_port = htons(p_port); // Short, network byte order. if (p_ip.is_valid()) { memcpy(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4); @@ -155,26 +120,13 @@ NetSocket *NetSocketPosix::_create_func() { } void NetSocketPosix::make_default() { -#if defined(WINDOWS_ENABLED) - if (_create == nullptr) { - WSADATA data; - WSAStartup(MAKEWORD(2, 2), &data); - } -#endif _create = _create_func; } void NetSocketPosix::cleanup() { -#if defined(WINDOWS_ENABLED) - if (_create != nullptr) { - WSACleanup(); - } - _create = nullptr; -#endif } -NetSocketPosix::NetSocketPosix() : - _sock(SOCK_EMPTY) { +NetSocketPosix::NetSocketPosix() { } NetSocketPosix::~NetSocketPosix() { @@ -189,29 +141,6 @@ NetSocketPosix::~NetSocketPosix() { #endif NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const { -#if defined(WINDOWS_ENABLED) - int err = WSAGetLastError(); - if (err == WSAEISCONN) { - return ERR_NET_IS_CONNECTED; - } - if (err == WSAEINPROGRESS || err == WSAEALREADY) { - return ERR_NET_IN_PROGRESS; - } - 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; - } - if (err == WSAEMSGSIZE || err == WSAENOBUFS) { - return ERR_NET_BUFFER_TOO_SMALL; - } - print_verbose("Socket error: " + itos(err)); - return ERR_NET_OTHER; -#else if (errno == EISCONN) { return ERR_NET_IS_CONNECTED; } @@ -230,9 +159,8 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const { if (errno == ENOBUFS) { return ERR_NET_BUFFER_TOO_SMALL; } - print_verbose("Socket error: " + itos(errno)); + print_verbose("Socket error: " + itos(errno) + "."); return ERR_NET_OTHER; -#endif } #if defined(__GNUC__) && !defined(__clang__) @@ -254,7 +182,7 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, Str ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); ERR_FAIL_COND_V(!_can_use_ip(p_ip, false), ERR_INVALID_PARAMETER); - // Need to force level and af_family to IP(v4) when using dual stacking and provided multicast group is IPv4 + // Need to force level and af_family to IP(v4) when using dual stacking and provided multicast group is IPv4. IP::Type type = _ip_type == IP::TYPE_ANY && p_ip.is_ipv4() ? IP::TYPE_IPV4 : _ip_type; // This needs to be the proper level for the multicast group, no matter if the socket is dual stacking. int level = type == IP::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6; @@ -277,7 +205,7 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, Str for (const IPAddress &F : c.ip_addresses) { if (!F.is_ipv4()) { - continue; // Wrong IP type + continue; // Wrong IP type. } if_ip = F; break; @@ -304,7 +232,7 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, Str return OK; } -void NetSocketPosix::_set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream) { +void NetSocketPosix::_set_socket(int p_sock, IP::Type p_ip_type, bool p_is_stream) { _sock = p_sock; _ip_type = p_ip_type; _is_stream = p_is_stream; @@ -313,11 +241,9 @@ void NetSocketPosix::_set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_ } void NetSocketPosix::_set_close_exec_enabled(bool p_enabled) { -#ifndef WINDOWS_ENABLED // Enable close on exec to avoid sharing with subprocesses. Off by default on Windows. int opts = fcntl(_sock, F_GETFD); fcntl(_sock, F_SETFD, opts | FD_CLOEXEC); -#endif } Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { @@ -336,7 +262,7 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { int type = p_sock_type == TYPE_TCP ? SOCK_STREAM : SOCK_DGRAM; _sock = socket(family, type, protocol); - if (_sock == SOCK_EMPTY && ip_type == IP::TYPE_ANY) { + if (_sock == -1 && ip_type == IP::TYPE_ANY) { // Careful here, changing the referenced parameter so the caller knows that we are using an IPv4 socket // in place of a dual stack one, and further calls to _set_sock_addr will work as expected. ip_type = IP::TYPE_IPV4; @@ -344,11 +270,11 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { _sock = socket(family, type, protocol); } - ERR_FAIL_COND_V(_sock == SOCK_EMPTY, FAILED); + ERR_FAIL_COND_V(_sock == -1, FAILED); _ip_type = ip_type; if (family == AF_INET6) { - // Select IPv4 over IPv6 mapping + // Select IPv4 over IPv6 mapping. set_ipv6_only_enabled(ip_type != IP::TYPE_ANY); } @@ -363,36 +289,22 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { // Disable descriptor sharing with subprocesses. _set_close_exec_enabled(true); -#if defined(WINDOWS_ENABLED) - if (!_is_stream) { - // Disable windows feature/bug reporting WSAECONNRESET/WSAENETRESET when - // recv/recvfrom and an ICMP reply was received from a previous send/sendto. - unsigned long disable = 0; - if (ioctlsocket(_sock, SIO_UDP_CONNRESET, &disable) == SOCKET_ERROR) { - print_verbose("Unable to turn off UDP WSAECONNRESET behavior on Windows"); - } - if (ioctlsocket(_sock, SIO_UDP_NETRESET, &disable) == SOCKET_ERROR) { - // This feature seems not to be supported on wine. - print_verbose("Unable to turn off UDP WSAENETRESET behavior on Windows"); - } - } -#endif #if defined(SO_NOSIGPIPE) - // Disable SIGPIPE (should only be relevant to stream sockets, but seems to affect UDP too on iOS) + // Disable SIGPIPE (should only be relevant to stream sockets, but seems to affect UDP too on iOS). int par = 1; - if (setsockopt(_sock, SOL_SOCKET, SO_NOSIGPIPE, SOCK_CBUF(&par), sizeof(int)) != 0) { - print_verbose("Unable to turn off SIGPIPE on socket"); + if (setsockopt(_sock, SOL_SOCKET, SO_NOSIGPIPE, &par, sizeof(int)) != 0) { + print_verbose("Unable to turn off SIGPIPE on socket."); } #endif return OK; } void NetSocketPosix::close() { - if (_sock != SOCK_EMPTY) { - SOCK_CLOSE(_sock); + if (_sock != -1) { + ::close(_sock); } - _sock = SOCK_EMPTY; + _sock = -1; _ip_type = IP::TYPE_NONE; _is_stream = false; } @@ -406,7 +318,7 @@ Error NetSocketPosix::bind(IPAddress p_addr, uint16_t p_port) { if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) { NetError err = _get_socket_error(); - print_verbose("Failed to bind socket. Error: " + itos(err)); + print_verbose("Failed to bind socket. Error: " + itos(err) + "."); close(); return ERR_UNAVAILABLE; } @@ -434,19 +346,19 @@ Error NetSocketPosix::connect_to_host(IPAddress p_host, uint16_t p_port) { struct sockaddr_storage addr; size_t addr_size = _set_addr_storage(&addr, p_host, p_port, _ip_type); - if (SOCK_CONNECT(_sock, (struct sockaddr *)&addr, addr_size) != 0) { + if (::connect(_sock, (struct sockaddr *)&addr, addr_size) != 0) { NetError err = _get_socket_error(); switch (err) { - // We are already connected + // We are already connected. case ERR_NET_IS_CONNECTED: return OK; - // Still waiting to connect, try again in a while + // Still waiting to connect, try again in a while. case ERR_NET_WOULD_BLOCK: case ERR_NET_IN_PROGRESS: return ERR_BUSY; default: - print_verbose("Connection to remote host failed!"); + print_verbose("Connection to remote host failed."); close(); return FAILED; } @@ -458,63 +370,6 @@ Error NetSocketPosix::connect_to_host(IPAddress p_host, uint16_t p_port) { Error NetSocketPosix::poll(PollType p_type, int p_timeout) const { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); -#if defined(WINDOWS_ENABLED) - bool ready = false; - fd_set rd, wr, ex; - fd_set *rdp = nullptr; - fd_set *wrp = nullptr; - FD_ZERO(&rd); - FD_ZERO(&wr); - FD_ZERO(&ex); - FD_SET(_sock, &ex); - struct timeval timeout = { p_timeout / 1000, (p_timeout % 1000) * 1000 }; - // For blocking operation, pass nullptr timeout pointer to select. - struct timeval *tp = nullptr; - if (p_timeout >= 0) { - // If timeout is non-negative, we want to specify the timeout instead. - tp = &timeout; - } - - switch (p_type) { - case POLL_TYPE_IN: - FD_SET(_sock, &rd); - rdp = &rd; - break; - case POLL_TYPE_OUT: - FD_SET(_sock, &wr); - wrp = ≀ - break; - case POLL_TYPE_IN_OUT: - FD_SET(_sock, &rd); - FD_SET(_sock, &wr); - rdp = &rd; - wrp = ≀ - } - int ret = select(1, rdp, wrp, &ex, tp); - - if (ret == SOCKET_ERROR) { - return FAILED; - } - - if (ret == 0) { - return ERR_BUSY; - } - - if (FD_ISSET(_sock, &ex)) { - _get_socket_error(); - print_verbose("Exception when polling socket."); - return FAILED; - } - - if (rdp && FD_ISSET(_sock, rdp)) { - ready = true; - } - if (wrp && FD_ISSET(_sock, wrp)) { - ready = true; - } - - return ready ? OK : ERR_BUSY; -#else struct pollfd pfd; pfd.fd = _sock; pfd.events = POLLIN; @@ -544,13 +399,12 @@ Error NetSocketPosix::poll(PollType p_type, int p_timeout) const { } return OK; -#endif } Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); - r_read = ::recv(_sock, SOCK_BUF(p_buffer), p_len, 0); + r_read = ::recv(_sock, p_buffer, p_len, 0); if (r_read < 0) { NetError err = _get_socket_error(); @@ -575,7 +429,7 @@ Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddr socklen_t len = sizeof(struct sockaddr_storage); memset(&from, 0, len); - r_read = ::recvfrom(_sock, SOCK_BUF(p_buffer), p_len, p_peek ? MSG_PEEK : 0, (struct sockaddr *)&from, &len); + r_read = ::recvfrom(_sock, p_buffer, p_len, p_peek ? MSG_PEEK : 0, (struct sockaddr *)&from, &len); if (r_read < 0) { NetError err = _get_socket_error(); @@ -615,7 +469,7 @@ Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) { flags = MSG_NOSIGNAL; } #endif - r_sent = ::send(_sock, SOCK_CBUF(p_buffer), p_len, flags); + r_sent = ::send(_sock, p_buffer, p_len, flags); if (r_sent < 0) { NetError err = _get_socket_error(); @@ -637,7 +491,7 @@ Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP struct sockaddr_storage addr; size_t addr_size = _set_addr_storage(&addr, p_ip, p_port, _ip_type); - r_sent = ::sendto(_sock, SOCK_CBUF(p_buffer), p_len, 0, (struct sockaddr *)&addr, addr_size); + r_sent = ::sendto(_sock, p_buffer, p_len, 0, (struct sockaddr *)&addr, addr_size); if (r_sent < 0) { NetError err = _get_socket_error(); @@ -662,8 +516,8 @@ Error NetSocketPosix::set_broadcasting_enabled(bool p_enabled) { } int par = p_enabled ? 1 : 0; - if (setsockopt(_sock, SOL_SOCKET, SO_BROADCAST, SOCK_CBUF(&par), sizeof(int)) != 0) { - WARN_PRINT("Unable to change broadcast setting"); + if (setsockopt(_sock, SOL_SOCKET, SO_BROADCAST, &par, sizeof(int)) != 0) { + WARN_PRINT("Unable to change broadcast setting."); return FAILED; } return OK; @@ -673,20 +527,15 @@ void NetSocketPosix::set_blocking_enabled(bool p_enabled) { ERR_FAIL_COND(!is_open()); int ret = 0; -#if defined(WINDOWS_ENABLED) - unsigned long par = p_enabled ? 0 : 1; - ret = SOCK_IOCTL(_sock, FIONBIO, &par); -#else int opts = fcntl(_sock, F_GETFL); if (p_enabled) { ret = fcntl(_sock, F_SETFL, opts & ~O_NONBLOCK); } else { ret = fcntl(_sock, F_SETFL, opts | O_NONBLOCK); } -#endif if (ret != 0) { - WARN_PRINT("Unable to change non-block mode"); + WARN_PRINT("Unable to change non-block mode."); } } @@ -696,56 +545,39 @@ void NetSocketPosix::set_ipv6_only_enabled(bool p_enabled) { ERR_FAIL_COND(_ip_type == IP::TYPE_IPV4); int par = p_enabled ? 1 : 0; - if (setsockopt(_sock, IPPROTO_IPV6, IPV6_V6ONLY, SOCK_CBUF(&par), sizeof(int)) != 0) { - WARN_PRINT("Unable to change IPv4 address mapping over IPv6 option"); + if (setsockopt(_sock, IPPROTO_IPV6, IPV6_V6ONLY, &par, sizeof(int)) != 0) { + WARN_PRINT("Unable to change IPv4 address mapping over IPv6 option."); } } void NetSocketPosix::set_tcp_no_delay_enabled(bool p_enabled) { ERR_FAIL_COND(!is_open()); - ERR_FAIL_COND(!_is_stream); // Not TCP + ERR_FAIL_COND(!_is_stream); // Not TCP. int par = p_enabled ? 1 : 0; - if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, SOCK_CBUF(&par), sizeof(int)) < 0) { - ERR_PRINT("Unable to set TCP no delay option"); + if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, &par, sizeof(int)) < 0) { + WARN_PRINT("Unable to set TCP no delay option."); } } void NetSocketPosix::set_reuse_address_enabled(bool p_enabled) { ERR_FAIL_COND(!is_open()); -// On Windows, enabling SO_REUSEADDR actually would also enable reuse port, very bad on TCP. Denying... -// Windows does not have this option, SO_REUSEADDR in this magical world means SO_REUSEPORT -#ifndef WINDOWS_ENABLED int par = p_enabled ? 1 : 0; - if (setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, SOCK_CBUF(&par), sizeof(int)) < 0) { - WARN_PRINT("Unable to set socket REUSEADDR option!"); - } -#endif -} - -void NetSocketPosix::set_reuse_port_enabled(bool p_enabled) { - ERR_FAIL_COND(!is_open()); - -// See comment above... -#ifdef WINDOWS_ENABLED -#define SO_REUSEPORT SO_REUSEADDR -#endif - int par = p_enabled ? 1 : 0; - if (setsockopt(_sock, SOL_SOCKET, SO_REUSEPORT, SOCK_CBUF(&par), sizeof(int)) < 0) { - WARN_PRINT("Unable to set socket REUSEPORT option!"); + if (setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, &par, sizeof(int)) < 0) { + WARN_PRINT("Unable to set socket REUSEADDR option."); } } bool NetSocketPosix::is_open() const { - return _sock != SOCK_EMPTY; + return _sock != -1; } int NetSocketPosix::get_available_bytes() const { ERR_FAIL_COND_V(!is_open(), -1); - SOCK_FIONREAD_LEN_TYPE len; - int ret = SOCK_IOCTL(_sock, FIONREAD, &len); + int len; + int ret = ioctl(_sock, FIONREAD, &len); if (ret == -1) { _get_socket_error(); print_verbose("Error when checking available bytes on socket."); @@ -774,8 +606,8 @@ Ref NetSocketPosix::accept(IPAddress &r_ip, uint16_t &r_port) { struct sockaddr_storage their_addr; socklen_t size = sizeof(their_addr); - SOCKET_TYPE fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size); - if (fd == SOCK_EMPTY) { + int fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size); + if (fd == -1) { _get_socket_error(); print_verbose("Error when accepting socket connection."); return out; @@ -797,4 +629,4 @@ Error NetSocketPosix::leave_multicast_group(const IPAddress &p_multi_address, co return _change_multicast_group(p_multi_address, p_if_name, false); } -#endif // UNIX_SOCKET_UNAVAILABLE +#endif // UNIX_ENABLED && !UNIX_SOCKET_UNAVAILABLE diff --git a/drivers/unix/net_socket_posix.h b/drivers/unix/net_socket_unix.h similarity index 72% rename from drivers/unix/net_socket_posix.h rename to drivers/unix/net_socket_unix.h index aa59ff36eed..22f7bfdd91f 100644 --- a/drivers/unix/net_socket_posix.h +++ b/drivers/unix/net_socket_unix.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* net_socket_posix.h */ +/* net_socket_unix.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,25 +28,18 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef NET_SOCKET_POSIX_H -#define NET_SOCKET_POSIX_H +#ifndef NET_SOCKET_UNIX_H +#define NET_SOCKET_UNIX_H + +#if defined(UNIX_ENABLED) && !defined(UNIX_SOCKET_UNAVAILABLE) #include "core/io/net_socket.h" -#if defined(WINDOWS_ENABLED) -#include -#include -#define SOCKET_TYPE SOCKET - -#else #include -#define SOCKET_TYPE int - -#endif class NetSocketPosix : public NetSocket { private: - SOCKET_TYPE _sock; // NOLINT - the default value is defined in the .cpp + int _sock = -1; IP::Type _ip_type = IP::TYPE_NONE; bool _is_stream = false; @@ -61,7 +54,7 @@ private: }; NetError _get_socket_error() const; - void _set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream); + void _set_socket(int p_sock, IP::Type p_ip_type, bool p_is_stream); _FORCE_INLINE_ Error _change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add); _FORCE_INLINE_ void _set_close_exec_enabled(bool p_enabled); @@ -76,33 +69,34 @@ public: static void _set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port); static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type); - virtual Error open(Type p_sock_type, IP::Type &ip_type); - virtual void close(); - virtual Error bind(IPAddress p_addr, uint16_t p_port); - virtual Error listen(int p_max_pending); - virtual Error connect_to_host(IPAddress p_host, uint16_t p_port); - virtual Error poll(PollType p_type, int timeout) const; - virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read); - virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false); - virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent); - virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port); - virtual Ref accept(IPAddress &r_ip, uint16_t &r_port); + virtual Error open(Type p_sock_type, IP::Type &ip_type) override; + virtual void close() override; + virtual Error bind(IPAddress p_addr, uint16_t p_port) override; + virtual Error listen(int p_max_pending) override; + virtual Error connect_to_host(IPAddress p_host, uint16_t p_port) override; + virtual Error poll(PollType p_type, int timeout) const override; + virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) override; + virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) override; + virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) override; + virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) override; + virtual Ref accept(IPAddress &r_ip, uint16_t &r_port) override; - virtual bool is_open() const; - virtual int get_available_bytes() const; - virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const; + virtual bool is_open() const override; + virtual int get_available_bytes() const override; + virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const override; - virtual Error set_broadcasting_enabled(bool p_enabled); - virtual void set_blocking_enabled(bool p_enabled); - virtual void set_ipv6_only_enabled(bool p_enabled); - virtual void set_tcp_no_delay_enabled(bool p_enabled); - virtual void set_reuse_address_enabled(bool p_enabled); - virtual void set_reuse_port_enabled(bool p_enabled); - virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name); - virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name); + virtual Error set_broadcasting_enabled(bool p_enabled) override; + virtual void set_blocking_enabled(bool p_enabled) override; + virtual void set_ipv6_only_enabled(bool p_enabled) override; + virtual void set_tcp_no_delay_enabled(bool p_enabled) override; + virtual void set_reuse_address_enabled(bool p_enabled) override; + virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override; + virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override; NetSocketPosix(); - ~NetSocketPosix(); + ~NetSocketPosix() override; }; -#endif // NET_SOCKET_POSIX_H +#endif // UNIX_ENABLED && !UNIX_SOCKET_UNAVAILABLE + +#endif // NET_SOCKET_UNIX_H diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 8a9b1300689..b23dba7a499 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -38,7 +38,7 @@ #include "drivers/unix/dir_access_unix.h" #include "drivers/unix/file_access_unix.h" #include "drivers/unix/file_access_unix_pipe.h" -#include "drivers/unix/net_socket_posix.h" +#include "drivers/unix/net_socket_unix.h" #include "drivers/unix/thread_posix.h" #include "servers/rendering_server.h" diff --git a/drivers/windows/net_socket_winsock.cpp b/drivers/windows/net_socket_winsock.cpp new file mode 100644 index 00000000000..3fe7fc619e9 --- /dev/null +++ b/drivers/windows/net_socket_winsock.cpp @@ -0,0 +1,613 @@ +/**************************************************************************/ +/* net_socket_winsock.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifdef WINDOWS_ENABLED + +#include "net_socket_winsock.h" + +#include +#include + +#include +// Workaround missing flag in MinGW +#if defined(__MINGW32__) && !defined(SIO_UDP_NETRESET) +#define SIO_UDP_NETRESET _WSAIOW(IOC_VENDOR, 15) +#endif + +size_t NetSocketWinSock::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) { + memset(p_addr, 0, sizeof(struct sockaddr_storage)); + if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket. + + // IPv6 only socket with IPv4 address. + ERR_FAIL_COND_V(!p_ip.is_wildcard() && p_ip_type == IP::TYPE_IPV6 && p_ip.is_ipv4(), 0); + + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; + addr6->sin6_family = AF_INET6; + addr6->sin6_port = htons(p_port); + if (p_ip.is_valid()) { + memcpy(&addr6->sin6_addr.s6_addr, p_ip.get_ipv6(), 16); + } else { + addr6->sin6_addr = in6addr_any; + } + return sizeof(sockaddr_in6); + } else { // IPv4 socket. + + // IPv4 socket with IPv6 address. + ERR_FAIL_COND_V(!p_ip.is_wildcard() && !p_ip.is_ipv4(), 0); + + struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; + addr4->sin_family = AF_INET; + addr4->sin_port = htons(p_port); // Short, network byte order. + + if (p_ip.is_valid()) { + memcpy(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4); + } else { + addr4->sin_addr.s_addr = INADDR_ANY; + } + + return sizeof(sockaddr_in); + } +} + +void NetSocketWinSock::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port) { + if (p_addr->ss_family == AF_INET) { + struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; + 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; + if (r_ip) { + r_ip->set_ipv6(addr6->sin6_addr.s6_addr); + } + if (r_port) { + *r_port = ntohs(addr6->sin6_port); + } + } +} + +NetSocket *NetSocketWinSock::_create_func() { + return memnew(NetSocketWinSock); +} + +void NetSocketWinSock::make_default() { + ERR_FAIL_COND(_create != nullptr); + + WSADATA data; + WSAStartup(MAKEWORD(2, 2), &data); + _create = _create_func; +} + +void NetSocketWinSock::cleanup() { + ERR_FAIL_COND(_create == nullptr); + + WSACleanup(); + _create = nullptr; +} + +NetSocketWinSock::NetSocketWinSock() { +} + +NetSocketWinSock::~NetSocketWinSock() { + close(); +} + +NetSocketWinSock::NetError NetSocketWinSock::_get_socket_error() const { + int err = WSAGetLastError(); + if (err == WSAEISCONN) { + return ERR_NET_IS_CONNECTED; + } + if (err == WSAEINPROGRESS || err == WSAEALREADY) { + return ERR_NET_IN_PROGRESS; + } + 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; + } + if (err == WSAEMSGSIZE || err == WSAENOBUFS) { + return ERR_NET_BUFFER_TOO_SMALL; + } + print_verbose("Socket error: " + itos(err) + "."); + return ERR_NET_OTHER; +} + +bool NetSocketWinSock::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const { + if (p_for_bind && !(p_ip.is_valid() || p_ip.is_wildcard())) { + return false; + } else if (!p_for_bind && !p_ip.is_valid()) { + return false; + } + // Check if socket support this IP type. + IP::Type type = p_ip.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + return !(_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type); +} + +_FORCE_INLINE_ Error NetSocketWinSock::_change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!_can_use_ip(p_ip, false), ERR_INVALID_PARAMETER); + + // Need to force level and af_family to IP(v4) when using dual stacking and provided multicast group is IPv4. + IP::Type type = _ip_type == IP::TYPE_ANY && p_ip.is_ipv4() ? IP::TYPE_IPV4 : _ip_type; + // This needs to be the proper level for the multicast group, no matter if the socket is dual stacking. + int level = type == IP::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6; + int ret = -1; + + IPAddress if_ip; + uint32_t if_v6id = 0; + HashMap if_info; + IP::get_singleton()->get_local_interfaces(&if_info); + for (KeyValue &E : if_info) { + IP::Interface_Info &c = E.value; + if (c.name != p_if_name) { + continue; + } + + if_v6id = (uint32_t)c.index.to_int(); + if (type == IP::TYPE_IPV6) { + break; // IPv6 uses index. + } + + for (const IPAddress &F : c.ip_addresses) { + if (!F.is_ipv4()) { + continue; // Wrong IP type. + } + if_ip = F; + break; + } + break; + } + + if (level == IPPROTO_IP) { + ERR_FAIL_COND_V(!if_ip.is_valid(), ERR_INVALID_PARAMETER); + struct ip_mreq greq; + int sock_opt = p_add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; + memcpy(&greq.imr_multiaddr, p_ip.get_ipv4(), 4); + memcpy(&greq.imr_interface, if_ip.get_ipv4(), 4); + ret = setsockopt(_sock, level, sock_opt, (const char *)&greq, sizeof(greq)); + } else { + struct ipv6_mreq greq; + int sock_opt = p_add ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP; + memcpy(&greq.ipv6mr_multiaddr, p_ip.get_ipv6(), 16); + greq.ipv6mr_interface = if_v6id; + ret = setsockopt(_sock, level, sock_opt, (const char *)&greq, sizeof(greq)); + } + ERR_FAIL_COND_V(ret != 0, FAILED); + + return OK; +} + +void NetSocketWinSock::_set_socket(SOCKET p_sock, IP::Type p_ip_type, bool p_is_stream) { + _sock = p_sock; + _ip_type = p_ip_type; + _is_stream = p_is_stream; +} + +Error NetSocketWinSock::open(Type p_sock_type, IP::Type &ip_type) { + ERR_FAIL_COND_V(is_open(), ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(ip_type > IP::TYPE_ANY || ip_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER); + + int family = ip_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6; + int protocol = p_sock_type == TYPE_TCP ? IPPROTO_TCP : IPPROTO_UDP; + int type = p_sock_type == TYPE_TCP ? SOCK_STREAM : SOCK_DGRAM; + _sock = socket(family, type, protocol); + + if (_sock == INVALID_SOCKET && ip_type == IP::TYPE_ANY) { + // Careful here, changing the referenced parameter so the caller knows that we are using an IPv4 socket + // in place of a dual stack one, and further calls to _set_sock_addr will work as expected. + ip_type = IP::TYPE_IPV4; + family = AF_INET; + _sock = socket(family, type, protocol); + } + + ERR_FAIL_COND_V(_sock == INVALID_SOCKET, FAILED); + _ip_type = ip_type; + + if (family == AF_INET6) { + // Select IPv4 over IPv6 mapping. + set_ipv6_only_enabled(ip_type != IP::TYPE_ANY); + } + + if (protocol == IPPROTO_UDP) { + // Make sure to disable broadcasting for UDP sockets. + // Depending on the OS, this option might or might not be enabled by default. Let's normalize it. + set_broadcasting_enabled(false); + } + + _is_stream = p_sock_type == TYPE_TCP; + + if (!_is_stream) { + // Disable windows feature/bug reporting WSAECONNRESET/WSAENETRESET when + // recv/recvfrom and an ICMP reply was received from a previous send/sendto. + unsigned long disable = 0; + if (ioctlsocket(_sock, SIO_UDP_CONNRESET, &disable) == SOCKET_ERROR) { + print_verbose("Unable to turn off UDP WSAECONNRESET behavior on Windows."); + } + if (ioctlsocket(_sock, SIO_UDP_NETRESET, &disable) == SOCKET_ERROR) { + // This feature seems not to be supported on wine. + print_verbose("Unable to turn off UDP WSAENETRESET behavior on Windows."); + } + } + return OK; +} + +void NetSocketWinSock::close() { + if (_sock != INVALID_SOCKET) { + closesocket(_sock); + } + + _sock = INVALID_SOCKET; + _ip_type = IP::TYPE_NONE; + _is_stream = false; +} + +Error NetSocketWinSock::bind(IPAddress p_addr, uint16_t p_port) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!_can_use_ip(p_addr, true), ERR_INVALID_PARAMETER); + + sockaddr_storage addr; + size_t addr_size = _set_addr_storage(&addr, p_addr, p_port, _ip_type); + + if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) { + NetError err = _get_socket_error(); + print_verbose("Failed to bind socket. Error: " + itos(err) + "."); + close(); + return ERR_UNAVAILABLE; + } + + return OK; +} + +Error NetSocketWinSock::listen(int p_max_pending) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + if (::listen(_sock, p_max_pending) != 0) { + _get_socket_error(); + print_verbose("Failed to listen from socket."); + close(); + return FAILED; + } + + return OK; +} + +Error NetSocketWinSock::connect_to_host(IPAddress p_host, uint16_t p_port) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!_can_use_ip(p_host, false), ERR_INVALID_PARAMETER); + + struct sockaddr_storage addr; + size_t addr_size = _set_addr_storage(&addr, p_host, p_port, _ip_type); + + if (::WSAConnect(_sock, (struct sockaddr *)&addr, addr_size, nullptr, nullptr, nullptr, nullptr) != 0) { + NetError err = _get_socket_error(); + + switch (err) { + // We are already connected. + case ERR_NET_IS_CONNECTED: + return OK; + // Still waiting to connect, try again in a while. + case ERR_NET_WOULD_BLOCK: + case ERR_NET_IN_PROGRESS: + return ERR_BUSY; + default: + print_verbose("Connection to remote host failed."); + close(); + return FAILED; + } + } + + return OK; +} + +Error NetSocketWinSock::poll(PollType p_type, int p_timeout) const { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + bool ready = false; + fd_set rd, wr, ex; + fd_set *rdp = nullptr; + fd_set *wrp = nullptr; + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + FD_SET(_sock, &ex); + struct timeval timeout = { p_timeout / 1000, (p_timeout % 1000) * 1000 }; + // For blocking operation, pass nullptr timeout pointer to select. + struct timeval *tp = nullptr; + if (p_timeout >= 0) { + // If timeout is non-negative, we want to specify the timeout instead. + tp = &timeout; + } + + switch (p_type) { + case POLL_TYPE_IN: + FD_SET(_sock, &rd); + rdp = &rd; + break; + case POLL_TYPE_OUT: + FD_SET(_sock, &wr); + wrp = ≀ + break; + case POLL_TYPE_IN_OUT: + FD_SET(_sock, &rd); + FD_SET(_sock, &wr); + rdp = &rd; + wrp = ≀ + } + // WSAPoll is broken: https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/. + int ret = select(1, rdp, wrp, &ex, tp); + + if (ret == SOCKET_ERROR) { + return FAILED; + } + + if (ret == 0) { + return ERR_BUSY; + } + + if (FD_ISSET(_sock, &ex)) { + _get_socket_error(); + print_verbose("Exception when polling socket."); + return FAILED; + } + + if (rdp && FD_ISSET(_sock, rdp)) { + ready = true; + } + if (wrp && FD_ISSET(_sock, wrp)) { + ready = true; + } + + return ready ? OK : ERR_BUSY; +} + +Error NetSocketWinSock::recv(uint8_t *p_buffer, int p_len, int &r_read) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + r_read = ::recv(_sock, (char *)p_buffer, p_len, 0); + + if (r_read < 0) { + NetError err = _get_socket_error(); + if (err == ERR_NET_WOULD_BLOCK) { + return ERR_BUSY; + } + + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } + + return FAILED; + } + + return OK; +} + +Error NetSocketWinSock::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + struct sockaddr_storage from; + socklen_t len = sizeof(struct sockaddr_storage); + memset(&from, 0, len); + + r_read = ::recvfrom(_sock, (char *)p_buffer, p_len, p_peek ? MSG_PEEK : 0, (struct sockaddr *)&from, &len); + + if (r_read < 0) { + NetError err = _get_socket_error(); + if (err == ERR_NET_WOULD_BLOCK) { + return ERR_BUSY; + } + + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } + + return FAILED; + } + + if (from.ss_family == AF_INET) { + struct sockaddr_in *sin_from = (struct sockaddr_in *)&from; + r_ip.set_ipv4((uint8_t *)&sin_from->sin_addr); + r_port = ntohs(sin_from->sin_port); + } else if (from.ss_family == AF_INET6) { + struct sockaddr_in6 *s6_from = (struct sockaddr_in6 *)&from; + r_ip.set_ipv6((uint8_t *)&s6_from->sin6_addr); + r_port = ntohs(s6_from->sin6_port); + } else { + // Unsupported socket family, should never happen. + ERR_FAIL_V(FAILED); + } + + return OK; +} + +Error NetSocketWinSock::send(const uint8_t *p_buffer, int p_len, int &r_sent) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + int flags = 0; + r_sent = ::send(_sock, (const char *)p_buffer, p_len, flags); + + if (r_sent < 0) { + NetError err = _get_socket_error(); + if (err == ERR_NET_WOULD_BLOCK) { + return ERR_BUSY; + } + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } + + return FAILED; + } + + return OK; +} + +Error NetSocketWinSock::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + struct sockaddr_storage addr; + size_t addr_size = _set_addr_storage(&addr, p_ip, p_port, _ip_type); + r_sent = ::sendto(_sock, (const char *)p_buffer, p_len, 0, (struct sockaddr *)&addr, addr_size); + + if (r_sent < 0) { + NetError err = _get_socket_error(); + if (err == ERR_NET_WOULD_BLOCK) { + return ERR_BUSY; + } + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } + + return FAILED; + } + + return OK; +} + +Error NetSocketWinSock::set_broadcasting_enabled(bool p_enabled) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + // IPv6 has no broadcast support. + if (_ip_type == IP::TYPE_IPV6) { + return ERR_UNAVAILABLE; + } + + int par = p_enabled ? 1 : 0; + if (setsockopt(_sock, SOL_SOCKET, SO_BROADCAST, (const char *)&par, sizeof(int)) != 0) { + WARN_PRINT("Unable to change broadcast setting."); + return FAILED; + } + return OK; +} + +void NetSocketWinSock::set_blocking_enabled(bool p_enabled) { + ERR_FAIL_COND(!is_open()); + + int ret = 0; + unsigned long par = p_enabled ? 0 : 1; + ret = ioctlsocket(_sock, FIONBIO, &par); + if (ret != 0) { + WARN_PRINT("Unable to change non-block mode."); + } +} + +void NetSocketWinSock::set_ipv6_only_enabled(bool p_enabled) { + ERR_FAIL_COND(!is_open()); + // This option is only available in IPv6 sockets. + ERR_FAIL_COND(_ip_type == IP::TYPE_IPV4); + + int par = p_enabled ? 1 : 0; + if (setsockopt(_sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&par, sizeof(int)) != 0) { + WARN_PRINT("Unable to change IPv4 address mapping over IPv6 option."); + } +} + +void NetSocketWinSock::set_tcp_no_delay_enabled(bool p_enabled) { + ERR_FAIL_COND(!is_open()); + ERR_FAIL_COND(!_is_stream); // Not TCP. + + int par = p_enabled ? 1 : 0; + if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&par, sizeof(int)) < 0) { + WARN_PRINT("Unable to set TCP no delay option."); + } +} + +void NetSocketWinSock::set_reuse_address_enabled(bool p_enabled) { + ERR_FAIL_COND(!is_open()); + + // On Windows, enabling SO_REUSEADDR actually would also enable reuse port, very bad on TCP. Denying... + // Windows does not have this option, SO_REUSEADDR in this magical world means SO_REUSEPORT +} + +bool NetSocketWinSock::is_open() const { + return _sock != INVALID_SOCKET; +} + +int NetSocketWinSock::get_available_bytes() const { + ERR_FAIL_COND_V(!is_open(), -1); + + unsigned long len; + int ret = ioctlsocket(_sock, FIONREAD, &len); + if (ret == -1) { + _get_socket_error(); + print_verbose("Error when checking available bytes on socket."); + return -1; + } + return len; +} + +Error NetSocketWinSock::get_socket_address(IPAddress *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 NetSocketWinSock::accept(IPAddress &r_ip, uint16_t &r_port) { + Ref out; + ERR_FAIL_COND_V(!is_open(), out); + + struct sockaddr_storage their_addr; + socklen_t size = sizeof(their_addr); + SOCKET fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size); + if (fd == INVALID_SOCKET) { + _get_socket_error(); + print_verbose("Error when accepting socket connection."); + return out; + } + + _set_ip_port(&their_addr, &r_ip, &r_port); + + NetSocketWinSock *ns = memnew(NetSocketWinSock); + ns->_set_socket(fd, _ip_type, _is_stream); + ns->set_blocking_enabled(false); + return Ref(ns); +} + +Error NetSocketWinSock::join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) { + return _change_multicast_group(p_multi_address, p_if_name, true); +} + +Error NetSocketWinSock::leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) { + return _change_multicast_group(p_multi_address, p_if_name, false); +} + +#endif // WINDOWS_ENABLED diff --git a/drivers/windows/net_socket_winsock.h b/drivers/windows/net_socket_winsock.h new file mode 100644 index 00000000000..5c3445b8cbc --- /dev/null +++ b/drivers/windows/net_socket_winsock.h @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* net_socket_winsock.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef NET_SOCKET_WINSOCK_H +#define NET_SOCKET_WINSOCK_H + +#ifdef WINDOWS_ENABLED + +#include "core/io/net_socket.h" + +#include +#include + +class NetSocketWinSock : public NetSocket { +private: + SOCKET _sock = INVALID_SOCKET; + IP::Type _ip_type = IP::TYPE_NONE; + bool _is_stream = false; + + enum NetError { + ERR_NET_WOULD_BLOCK, + ERR_NET_IS_CONNECTED, + ERR_NET_IN_PROGRESS, + ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE, + ERR_NET_UNAUTHORIZED, + ERR_NET_BUFFER_TOO_SMALL, + ERR_NET_OTHER, + }; + + NetError _get_socket_error() const; + void _set_socket(SOCKET p_sock, IP::Type p_ip_type, bool p_is_stream); + _FORCE_INLINE_ Error _change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add); + +protected: + static NetSocket *_create_func(); + + bool _can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const; + +public: + static void make_default(); + static void cleanup(); + static void _set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port); + static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type); + + virtual Error open(Type p_sock_type, IP::Type &ip_type) override; + virtual void close() override; + virtual Error bind(IPAddress p_addr, uint16_t p_port) override; + virtual Error listen(int p_max_pending) override; + virtual Error connect_to_host(IPAddress p_host, uint16_t p_port) override; + virtual Error poll(PollType p_type, int timeout) const override; + virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) override; + virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) override; + virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) override; + virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) override; + virtual Ref accept(IPAddress &r_ip, uint16_t &r_port) override; + + virtual bool is_open() const override; + virtual int get_available_bytes() const override; + virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const override; + + virtual Error set_broadcasting_enabled(bool p_enabled) override; + virtual void set_blocking_enabled(bool p_enabled) override; + virtual void set_ipv6_only_enabled(bool p_enabled) override; + virtual void set_tcp_no_delay_enabled(bool p_enabled) override; + virtual void set_reuse_address_enabled(bool p_enabled) override; + virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override; + virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override; + + NetSocketWinSock(); + ~NetSocketWinSock() override; +}; + +#endif // WINDOWS_ENABLED + +#endif // NET_SOCKET_WINSOCK_H diff --git a/platform/android/net_socket_android.h b/platform/android/net_socket_android.h index 26cb2d4e3d4..452553ab1cf 100644 --- a/platform/android/net_socket_android.h +++ b/platform/android/net_socket_android.h @@ -31,7 +31,7 @@ #ifndef NET_SOCKET_ANDROID_H #define NET_SOCKET_ANDROID_H -#include "drivers/unix/net_socket_posix.h" +#include "drivers/unix/net_socket_unix.h" #include diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index bff34432148..50fc4e3653b 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -40,10 +40,10 @@ #include "core/debugger/script_debugger.h" #include "core/io/marshalls.h" #include "core/version_generated.gen.h" -#include "drivers/unix/net_socket_posix.h" #include "drivers/windows/dir_access_windows.h" #include "drivers/windows/file_access_windows.h" #include "drivers/windows/file_access_windows_pipe.h" +#include "drivers/windows/net_socket_winsock.h" #include "main/main.h" #include "servers/audio_server.h" #include "servers/rendering/rendering_server_default.h" @@ -209,7 +209,7 @@ void OS_Windows::initialize() { DirAccess::make_default(DirAccess::ACCESS_USERDATA); DirAccess::make_default(DirAccess::ACCESS_FILESYSTEM); - NetSocketPosix::make_default(); + NetSocketWinSock::make_default(); // We need to know how often the clock is updated QueryPerformanceFrequency((LARGE_INTEGER *)&ticks_per_second); @@ -303,7 +303,7 @@ void OS_Windows::finalize_core() { timeEndPeriod(1); memdelete(process_map); - NetSocketPosix::cleanup(); + NetSocketWinSock::cleanup(); #ifdef WINDOWS_DEBUG_OUTPUT_ENABLED remove_error_handler(&error_handlers);