Download raw body.
[patch]: Don't let prosody hang on bad connections
On Thu, Feb 13, 2025 at 05:17:36PM +0100, Martijn van Duren wrote:
> Hello ports@,
>
> Some time ago I hit an issue where my prosody server randomly started to
> hang on the netio channel. After some debugging I found out that luasec
> puts the underlying socket in blocking mode before calling SSL_shutdown.
> This combined with a crappy client who is on the disconnect path can
> result in the server not being able to send out the shutdown
> notification, and waiting until the underlying TCP layer gives up.
>
> The diffs below is a backport from what is aleady accepted upstream by
> prosody[0], and the luasec diff as forwarded to the luasec project via
> prosody[1].
At the top of the patch files, can you add the URL where the patch
came from?
OK caspar@ with that fixed.
> Since OpenBSD appears to be disproportionately affected by issue, from
> what I've been told by the prosody people, it might be a good idea to
> ship these changes as local patches until both upstreams makes the
> new releases.
>
> martijn@
>
> [0] https://hg.prosody.im/trunk/rev/a08065207ef0
> [1] https://github.com/lunarmodules/luasec/pull/203/files
>
> diff /usr/ports
> path + /usr/ports
> commit - bfce4c31b71ca3f1fbcd26b4c1c97d12bb453e72
> blob - 9c2b8e2f66fcd4d47cb882e8cc8f95fd5cc1a410
> file + net/prosody/Makefile
> --- net/prosody/Makefile
> +++ net/prosody/Makefile
> @@ -1,5 +1,6 @@
> COMMENT = communications server for Jabber/XMPP written in Lua
> DISTNAME = prosody-0.12.5
> +REVISION = 0
> CATEGORIES = net
> HOMEPAGE = https://prosody.im/
>
> commit - bfce4c31b71ca3f1fbcd26b4c1c97d12bb453e72
> blob - /dev/null
> file + net/prosody/patches/patch-net_server_epoll_lua (mode 644)
> --- /dev/null
> +++ net/prosody/patches/patch-net_server_epoll_lua
> @@ -0,0 +1,45 @@
> +Index: net/server_epoll.lua
> +--- net/server_epoll.lua.orig
> ++++ net/server_epoll.lua
> +@@ -576,6 +576,7 @@ interface.send = interface.write;
> +
> + -- Close, possibly after writing is done
> + function interface:close()
> ++ local status, err;
> + if self._connected and self.writebuffer and (self.writebuffer[1] or type(self.writebuffer) == "string") then
> + self._connected = false;
> + self:set(false, true); -- Flush final buffer contents
> +@@ -584,6 +585,24 @@ function interface:close()
> + self.write, self.send = noop, noop; -- No more writing
> + self:debug("Close after writing remaining buffered data");
> + self.ondrain = interface.close;
> ++ elseif self.conn.shutdown and self._tls then
> ++ status, err = self.conn:shutdown();
> ++ self.onreadable = interface.close;
> ++ self.onwritable = interface.close;
> ++ if err == nil then
> ++ if status == true then
> ++ self._tls = false;
> ++ end
> ++ return self:close();
> ++ elseif err == "wantread" then
> ++ self:set(true, nil);
> ++ self:setreadtimeout();
> ++ elseif err == "wantwrite" then
> ++ self:set(nil, true);
> ++ self:setwritetimeout();
> ++ else
> ++ self._tls = false;
> ++ end
> + else
> + self:debug("Closing now");
> + self.write, self.send = noop, noop;
> +@@ -594,6 +613,8 @@ function interface:close()
> + end
> +
> + function interface:destroy()
> ++ -- make sure tls sockets aren't put in blocking mode
> ++ if self.conn.shutdown and self._tls then self.conn:shutdown(); end
> + self:del();
> + self:setwritetimeout(false);
> + self:setreadtimeout(false);
> commit - bfce4c31b71ca3f1fbcd26b4c1c97d12bb453e72
> blob - 98f317ac9f1568932b9ce2de581924d3e193c310
> file + security/luasec/Makefile
> --- security/luasec/Makefile
> +++ security/luasec/Makefile
> @@ -3,6 +3,7 @@ COMMENT= lua binding to OpenSSL to provide TLS/SSL com
> GH_ACCOUNT= brunoos
> GH_PROJECT= luasec
> GH_TAGNAME= v1.2.0
> +REVISION= 0
>
> CATEGORIES= security
>
> commit - bfce4c31b71ca3f1fbcd26b4c1c97d12bb453e72
> blob - /dev/null
> file + security/luasec/patches/patch-src_luasocket_usocket_c (mode 644)
> --- /dev/null
> +++ security/luasec/patches/patch-src_luasocket_usocket_c
> @@ -0,0 +1,11 @@
> +Index: src/luasocket/usocket.c
> +--- src/luasocket/usocket.c.orig
> ++++ src/luasocket/usocket.c
> +@@ -82,7 +82,6 @@ int socket_close(void) {
> + \*-------------------------------------------------------------------------*/
> + void socket_destroy(p_socket ps) {
> + if (*ps != SOCKET_INVALID) {
> +- socket_setblocking(ps);
> + close(*ps);
> + *ps = SOCKET_INVALID;
> + }
> commit - bfce4c31b71ca3f1fbcd26b4c1c97d12bb453e72
> blob - /dev/null
> file + security/luasec/patches/patch-src_ssl_c (mode 644)
> --- /dev/null
> +++ security/luasec/patches/patch-src_ssl_c
> @@ -0,0 +1,86 @@
> +Index: src/ssl.c
> +--- src/ssl.c.orig
> ++++ src/ssl.c
> +@@ -87,7 +87,8 @@ static int meth_destroy(lua_State *L)
> + {
> + p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection");
> + if (ssl->state == LSEC_STATE_CONNECTED) {
> +- socket_setblocking(&ssl->sock);
> ++ if (!ssl->shutdown)
> ++ socket_setblocking(&ssl->sock);
> + SSL_shutdown(ssl->ssl);
> + }
> + if (ssl->sock != SOCKET_INVALID) {
> +@@ -305,6 +306,7 @@ static int meth_create(lua_State *L)
> + return luaL_argerror(L, 1, "invalid context");
> + }
> + ssl->state = LSEC_STATE_NEW;
> ++ ssl->shutdown = 0;
> + SSL_set_fd(ssl->ssl, (int)SOCKET_INVALID);
> + SSL_set_mode(ssl->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE |
> + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
> +@@ -341,6 +343,56 @@ static int meth_receive(lua_State *L) {
> + }
> +
> + /**
> ++ * SSL shutdown function
> ++ */
> ++static int meth_shutdown(lua_State *L) {
> ++ p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, "SSL:Connection");
> ++ int err;
> ++ p_timeout tm = timeout_markstart(&ssl->tm);
> ++
> ++ ssl->shutdown = 1;
> ++ if (ssl->state == LSEC_STATE_CLOSED) {
> ++ lua_pushboolean(L, 1);
> ++ return 1;
> ++ }
> ++
> ++ err = SSL_shutdown(ssl->ssl);
> ++ switch (err) {
> ++ case 0:
> ++ lua_pushboolean(L, 0);
> ++ lua_pushnil(L);
> ++ return 2;
> ++ case 1:
> ++ lua_pushboolean(L, 1);
> ++ lua_pushnil(L);
> ++ ssl->state = LSEC_STATE_CLOSED;
> ++ return 2;
> ++ case -1:
> ++ lua_pushboolean(L, 0);
> ++ ssl->error = SSL_get_error(ssl->ssl, err);
> ++ switch (ssl->error) {
> ++ case SSL_ERROR_WANT_READ:
> ++ err = socket_waitfd(&ssl->sock, WAITFD_R, tm);
> ++ lua_pushstring(L, ssl_ioerror((void *)ssl, err == IO_TIMEOUT ? LSEC_IO_SSL : err));
> ++ break;
> ++ case SSL_ERROR_WANT_WRITE:
> ++ err = socket_waitfd(&ssl->sock, WAITFD_W, tm);
> ++ lua_pushstring(L, ssl_ioerror((void *)ssl, err == IO_TIMEOUT ? LSEC_IO_SSL : err));
> ++ break;
> ++ case SSL_ERROR_SYSCALL:
> ++ if (ERR_peek_error())
> ++ ssl->error = SSL_ERROR_SSL;
> ++ lua_pushstring(L, ssl_ioerror((void *)ssl, LSEC_IO_SSL));
> ++ break;
> ++ default:
> ++ lua_pushstring(L, ssl_ioerror((void *)ssl, LSEC_IO_SSL));
> ++ break;
> ++ }
> ++ return 2;
> ++ }
> ++}
> ++
> ++/**
> + * Get the buffer's statistics.
> + */
> + static int meth_getstats(lua_State *L) {
> +@@ -904,6 +956,7 @@ static int meth_tlsa(lua_State *L)
> + */
> + static luaL_Reg methods[] = {
> + {"close", meth_close},
> ++ {"shutdown", meth_shutdown},
> + {"getalpn", meth_getalpn},
> + {"getfd", meth_getfd},
> + {"getfinished", meth_getfinished},
> commit - bfce4c31b71ca3f1fbcd26b4c1c97d12bb453e72
> blob - /dev/null
> file + security/luasec/patches/patch-src_ssl_h (mode 644)
> --- /dev/null
> +++ security/luasec/patches/patch-src_ssl_h
> @@ -0,0 +1,11 @@
> +Index: src/ssl.h
> +--- src/ssl.h.orig
> ++++ src/ssl.h
> +@@ -32,6 +32,7 @@ typedef struct t_ssl_ {
> + t_timeout tm;
> + SSL *ssl;
> + int state;
> ++ int shutdown;
> + int error;
> + } t_ssl;
> + typedef t_ssl* p_ssl;
>
[patch]: Don't let prosody hang on bad connections