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]. 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;