From 8fbf5bef60568cd4dcd56921c37be64b8234d9bb Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Wed, 9 Dec 2020 17:13:34 +0100 Subject: Fix: workarounds for two emscripten bugs in the network stack --- src/network/core/address.cpp | 12 ++++++++++++ src/network/core/os_abstraction.h | 22 ++++++++++++++++++++++ src/network/core/tcp_listen.h | 3 +++ src/network/core/udp.cpp | 3 +++ 4 files changed, 40 insertions(+) (limited to 'src') diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index 1aaa0b5fb..2e00b5b19 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -267,6 +267,18 @@ SOCKET NetworkAddress::Resolve(int family, int socktype, int flags, SocketList * this->address_length = (int)runp->ai_addrlen; assert(sizeof(this->address) >= runp->ai_addrlen); memcpy(&this->address, runp->ai_addr, runp->ai_addrlen); +#ifdef __EMSCRIPTEN__ + /* Emscripten doesn't zero sin_zero, but as we compare addresses + * to see if they are the same address, we need them to be zero'd. + * Emscripten is, as far as we know, the only OS not doing this. + * + * https://github.com/emscripten-core/emscripten/issues/12998 + */ + if (this->address.ss_family == AF_INET) { + sockaddr_in *address_ipv4 = (sockaddr_in *)&this->address; + memset(address_ipv4->sin_zero, 0, sizeof(address_ipv4->sin_zero)); + } +#endif break; } diff --git a/src/network/core/os_abstraction.h b/src/network/core/os_abstraction.h index be8b8f919..8aa072aef 100644 --- a/src/network/core/os_abstraction.h +++ b/src/network/core/os_abstraction.h @@ -151,6 +151,28 @@ typedef unsigned long in_addr_t; #endif /* OS/2 */ +#ifdef __EMSCRIPTEN__ +/** + * Emscripten doesn't set 'addrlen' for accept(), getsockname(), getpeername() + * and recvfrom(), which confuses other functions and causes them to crash. + * This function needs to be called after these four functions to make sure + * 'addrlen' is patched up. + * + * https://github.com/emscripten-core/emscripten/issues/12996 + * + * @param address The address returned by those four functions. + * @return The correct value for addrlen. + */ +static inline socklen_t FixAddrLenForEmscripten(struct sockaddr_storage &address) +{ + switch (address.ss_family) { + case AF_INET6: return sizeof(struct sockaddr_in6); + case AF_INET: return sizeof(struct sockaddr_in); + default: NOT_REACHED(); + } +} +#endif + /** * Try to set the socket into non-blocking mode. * @param d The socket to set the non-blocking more for. diff --git a/src/network/core/tcp_listen.h b/src/network/core/tcp_listen.h index d2982363e..1f073aa73 100644 --- a/src/network/core/tcp_listen.h +++ b/src/network/core/tcp_listen.h @@ -42,6 +42,9 @@ public: socklen_t sin_len = sizeof(sin); SOCKET s = accept(ls, (struct sockaddr*)&sin, &sin_len); if (s == INVALID_SOCKET) return; +#ifdef __EMSCRIPTEN__ + sin_len = FixAddrLenForEmscripten(sin); +#endif SetNonBlocking(s); // XXX error handling? diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index 57352412b..aa6d39cbb 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -129,6 +129,9 @@ void NetworkUDPSocketHandler::ReceivePackets() /* Did we get the bytes for the base header of the packet? */ if (nbytes <= 0) break; // No data, i.e. no packet if (nbytes <= 2) continue; // Invalid data; try next packet +#ifdef __EMSCRIPTEN__ + client_len = FixAddrLenForEmscripten(client_addr); +#endif NetworkAddress address(client_addr, client_len); p.PrepareToRead(); -- cgit v1.2.3-54-g00ecf