summaryrefslogtreecommitdiff
path: root/drivers/unix/net_socket_posix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/unix/net_socket_posix.cpp')
-rw-r--r--drivers/unix/net_socket_posix.cpp129
1 files changed, 115 insertions, 14 deletions
diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp
index 2dffa083fe..5f99a40c79 100644
--- a/drivers/unix/net_socket_posix.cpp
+++ b/drivers/unix/net_socket_posix.cpp
@@ -30,6 +30,7 @@
#include "net_socket_posix.h"
+#ifndef UNIX_SOCKET_UNAVAILABLE
#if defined(UNIX_ENABLED)
#include <errno.h>
@@ -55,8 +56,12 @@
#include <netinet/tcp.h>
-#if defined(__APPLE__)
-#define MSG_NOSIGNAL SO_NOSIGPIPE
+// BSD calls this flag IPV6_JOIN_GROUP
+#if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP)
+#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
+#endif
+#if !defined(IPV6_DROP_MEMBERSHIP) && defined(IPV6_LEAVE_GROUP)
+#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
#endif
// Some custom defines to minimize ifdefs
@@ -65,6 +70,7 @@
#define SOCK_CBUF(x) x
#define SOCK_IOCTL ioctl
#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)
@@ -78,11 +84,10 @@
#define SOCK_CBUF(x) (const char *)(x)
#define SOCK_IOCTL ioctlsocket
#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, NULL, NULL, NULL, NULL)
-// Windows doesn't have this flag
-#ifndef MSG_NOSIGNAL
-#define MSG_NOSIGNAL 0
-#endif
// Workaround missing flag in MinGW
#if defined(__MINGW32__) && !defined(SIO_UDP_NETRESET)
#define SIO_UDP_NETRESET _WSAIOW(IOC_VENDOR, 15)
@@ -177,6 +182,13 @@ NetSocketPosix::~NetSocketPosix() {
close();
}
+// Silent a warning reported in #27594
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wlogical-op"
+#endif
+
NetSocketPosix::NetError NetSocketPosix::_get_socket_error() {
#if defined(WINDOWS_ENABLED)
int err = WSAGetLastError();
@@ -201,7 +213,11 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() {
#endif
}
-bool NetSocketPosix::_can_use_ip(const IP_Address p_ip, const bool p_for_bind) const {
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+bool NetSocketPosix::_can_use_ip(const IP_Address &p_ip, const bool p_for_bind) const {
if (p_for_bind && !(p_ip.is_valid() || p_ip.is_wildcard())) {
return false;
@@ -210,16 +226,80 @@ bool NetSocketPosix::_can_use_ip(const IP_Address p_ip, const bool p_for_bind) c
}
// Check if socket support this IP type.
IP::Type type = p_ip.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6;
- if (_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type) {
- return false;
+ return !(_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type);
+}
+
+_FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IP_Address 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;
+
+ IP_Address if_ip;
+ uint32_t if_v6id = 0;
+ Map<String, IP::Interface_Info> if_info;
+ IP::get_singleton()->get_local_interfaces(&if_info);
+ for (Map<String, IP::Interface_Info>::Element *E = if_info.front(); E; E = E->next()) {
+ IP::Interface_Info &c = E->get();
+ if (c.name != p_if_name)
+ continue;
+
+ if_v6id = (uint32_t)c.index.to_int64();
+ if (type == IP::TYPE_IPV6)
+ break; // IPv6 uses index.
+
+ for (List<IP_Address>::Element *F = c.ip_addresses.front(); F; F = F->next()) {
+ if (!F->get().is_ipv4())
+ continue; // Wrong IP type
+ if_ip = F->get();
+ 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;
+ copymem(&greq.imr_multiaddr, p_ip.get_ipv4(), 4);
+ copymem(&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;
+ copymem(&greq.ipv6mr_multiaddr, p_ip.get_ipv6(), 16);
+ greq.ipv6mr_interface = if_v6id;
+ ret = setsockopt(_sock, level, sock_opt, (const char *)&greq, sizeof(greq));
}
- return true;
+ ERR_FAIL_COND_V(ret != 0, FAILED);
+
+ return OK;
}
void NetSocketPosix::_set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream) {
_sock = p_sock;
_ip_type = p_ip_type;
_is_stream = p_is_stream;
+ // Disable descriptor sharing with subprocesses.
+ _set_close_exec_enabled(true);
+}
+
+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.
+#if defined(NO_FCNTL)
+ unsigned long par = p_enabled ? 1 : 0;
+ SOCK_IOCTL(_sock, FIOCLEX, &par);
+#else
+ int opts = fcntl(_sock, F_GETFD);
+ fcntl(_sock, F_SETFD, opts | FD_CLOEXEC);
+#endif
+#endif
}
Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) {
@@ -260,6 +340,9 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) {
_is_stream = p_sock_type == TYPE_TCP;
+ // 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
@@ -274,6 +357,13 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) {
}
}
#endif
+#if defined(SO_NOSIGPIPE)
+ // 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");
+ }
+#endif
return OK;
}
@@ -295,7 +385,7 @@ Error NetSocketPosix::bind(IP_Address p_addr, uint16_t p_port) {
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) == SOCK_EMPTY) {
+ if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
close();
ERR_FAIL_V(ERR_UNAVAILABLE);
}
@@ -306,7 +396,7 @@ Error NetSocketPosix::bind(IP_Address p_addr, uint16_t p_port) {
Error NetSocketPosix::listen(int p_max_pending) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
- if (::listen(_sock, p_max_pending) == SOCK_EMPTY) {
+ if (::listen(_sock, p_max_pending) != 0) {
close();
ERR_FAIL_V(FAILED);
@@ -323,7 +413,7 @@ Error NetSocketPosix::connect_to_host(IP_Address 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 (::connect(_sock, (struct sockaddr *)&addr, addr_size) == SOCK_EMPTY) {
+ if (SOCK_CONNECT(_sock, (struct sockaddr *)&addr, addr_size) != 0) {
NetError err = _get_socket_error();
@@ -410,7 +500,7 @@ Error NetSocketPosix::poll(PollType p_type, int p_timeout) const {
pfd.events = POLLOUT;
break;
case POLL_TYPE_IN_OUT:
- pfd.events = POLLOUT || POLLIN;
+ pfd.events = POLLOUT | POLLIN;
}
int ret = ::poll(&pfd, 1, p_timeout);
@@ -478,8 +568,10 @@ Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) {
ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED);
int flags = 0;
+#ifdef MSG_NOSIGNAL
if (_is_stream)
flags = MSG_NOSIGNAL;
+#endif
r_sent = ::send(_sock, SOCK_CBUF(p_buffer), p_len, flags);
if (r_sent < 0) {
@@ -614,3 +706,12 @@ Ref<NetSocket> NetSocketPosix::accept(IP_Address &r_ip, uint16_t &r_port) {
ns->set_blocking_enabled(false);
return Ref<NetSocket>(ns);
}
+
+Error NetSocketPosix::join_multicast_group(const IP_Address &p_multi_address, String p_if_name) {
+ return _change_multicast_group(p_multi_address, p_if_name, true);
+}
+
+Error NetSocketPosix::leave_multicast_group(const IP_Address &p_multi_address, String p_if_name) {
+ return _change_multicast_group(p_multi_address, p_if_name, false);
+}
+#endif