Hello community,

here is the log from the commit of package prosody for openSUSE:Factory checked 
in at 2020-10-02 17:39:32
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/prosody (Old)
 and      /work/SRC/openSUSE:Factory/.prosody.new.4249 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "prosody"

Fri Oct  2 17:39:32 2020 rev:21 rq:839107 version:0.11.7

Changes:
--------
--- /work/SRC/openSUSE:Factory/prosody/prosody.changes  2020-09-12 
00:11:41.493167003 +0200
+++ /work/SRC/openSUSE:Factory/.prosody.new.4249/prosody.changes        
2020-10-02 17:40:28.970852382 +0200
@@ -1,0 +2,13 @@
+Fri Oct  2 08:00:55 UTC 2020 - Michael Vetter <[email protected]>
+
+- Update to 0.11.7:
+  Security:
+  * mod_websocket: Enforce size limits on received frames (fixes #1593)
+  Fixes and improvements:
+  * mod_c2s, mod_s2s: Make stanza size limits configurable
+  * Add configuration options to control Lua garbage collection parameters
+  * net.http: Backport SNI support for outgoing HTTP requests (#409)
+  * mod_websocket: Process all data in the buffer on close frame and 
connection errors (fixes #1474, #1234)
+  * util.indexedbheap: Fix heap data structure corruption, causing some timers 
to fail after a reschedule (fixes #1572)
+
+-------------------------------------------------------------------

Old:
----
  prosody-0.11.6.tar.gz
  prosody-0.11.6.tar.gz.asc

New:
----
  prosody-0.11.7.tar.gz
  prosody-0.11.7.tar.gz.asc

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ prosody.spec ++++++
--- /var/tmp/diff_new_pack.LkP8WT/_old  2020-10-02 17:40:31.754854042 +0200
+++ /var/tmp/diff_new_pack.LkP8WT/_new  2020-10-02 17:40:31.758854045 +0200
@@ -18,7 +18,7 @@
 
 %define _piddir /run
 Name:           prosody
-Version:        0.11.6
+Version:        0.11.7
 Release:        0
 Summary:        Communications server for Jabber/XMPP
 License:        MIT

++++++ prosody-0.11.6.tar.gz -> prosody-0.11.7.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/.hg_archival.txt 
new/prosody-0.11.7/.hg_archival.txt
--- old/prosody-0.11.6/.hg_archival.txt 2020-08-01 12:58:37.000000000 +0200
+++ new/prosody-0.11.7/.hg_archival.txt 2020-05-31 22:39:34.000000000 +0200
@@ -1,4 +1,4 @@
 repo: 3e3171b59028ee70122cfec6ecf98f518f946b59
-node: bacca65ce107b8549ce5f9079e81e5771eed2021
+node: ece430d4980997b216c2240015bf922bdeb12dd6
 branch: 0.11
-tag: 0.11.6
+tag: 0.11.7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/net/http.lua 
new/prosody-0.11.7/net/http.lua
--- old/prosody-0.11.6/net/http.lua     2020-08-01 12:58:37.000000000 +0200
+++ new/prosody-0.11.7/net/http.lua     2020-05-31 22:39:34.000000000 +0200
@@ -272,7 +272,7 @@
                sslctx = ex and ex.sslctx or self.options and 
self.options.sslctx;
        end
 
-       local http_service = basic_resolver.new(host, port_number);
+       local http_service = basic_resolver.new(host, port_number, "tcp", { 
servername = req.host });
        connect(http_service, listener, { sslctx = sslctx }, req);
 
        self.events.fire_event("request", { http = self, request = req, url = u 
});
@@ -314,4 +314,7 @@
        formencode = util_http.formencode;
        formdecode = util_http.formdecode;
        destroy_request = destroy_request;
+       features = {
+               sni = true;
+       };
 };
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/net/server_epoll.lua 
new/prosody-0.11.7/net/server_epoll.lua
--- old/prosody-0.11.6/net/server_epoll.lua     2020-08-01 12:58:37.000000000 
+0200
+++ new/prosody-0.11.7/net/server_epoll.lua     2020-05-31 22:39:34.000000000 
+0200
@@ -483,6 +483,9 @@
                end
                conn:settimeout(0);
                self.conn = conn;
+               if conn.sni and self.servername then
+                       conn:sni(self.servername);
+               end
                self:on("starttls");
                self.ondrain = nil;
                self.onwritable = interface.tlshandskake;
@@ -512,7 +515,7 @@
        end
 end
 
-local function wrapsocket(client, server, read_size, listeners, tls_ctx) -- 
luasocket object -> interface object
+local function wrapsocket(client, server, read_size, listeners, tls_ctx, 
extra) -- luasocket object -> interface object
        client:settimeout(0);
        local conn = setmetatable({
                conn = client;
@@ -523,8 +526,15 @@
                writebuffer = {};
                tls_ctx = tls_ctx or (server and server.tls_ctx);
                tls_direct = server and server.tls_direct;
+               extra = extra;
        }, interface_mt);
 
+       if extra then
+               if extra.servername then
+                       conn.servername = extra.servername;
+               end
+       end
+
        conn:updatenames();
        return conn;
 end
@@ -617,8 +627,8 @@
 end
 
 -- COMPAT
-local function wrapclient(conn, addr, port, listeners, read_size, tls_ctx)
-       local client = wrapsocket(conn, nil, read_size, listeners, tls_ctx);
+local function wrapclient(conn, addr, port, listeners, read_size, tls_ctx, 
extra)
+       local client = wrapsocket(conn, nil, read_size, listeners, tls_ctx, 
extra);
        if not client.peername then
                client.peername, client.peerport = addr, port;
        end
@@ -631,7 +641,7 @@
 end
 
 -- New outgoing TCP connection
-local function addclient(addr, port, listeners, read_size, tls_ctx, typ)
+local function addclient(addr, port, listeners, read_size, tls_ctx, typ, extra)
        local create;
        if not typ then
                local n = inet_pton(addr);
@@ -653,7 +663,7 @@
        if not ok then return ok, err; end
        local ok, err = conn:setpeername(addr, port);
        if not ok and err ~= "timeout" then return ok, err; end
-       local client = wrapsocket(conn, nil, read_size, listeners, tls_ctx)
+       local client = wrapsocket(conn, nil, read_size, listeners, tls_ctx, 
extra)
        local ok, err = client:init();
        if not ok then return ok, err; end
        if tls_ctx then
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/net/server_event.lua 
new/prosody-0.11.7/net/server_event.lua
--- old/prosody-0.11.6/net/server_event.lua     2020-08-01 12:58:37.000000000 
+0200
+++ new/prosody-0.11.7/net/server_event.lua     2020-05-31 22:39:34.000000000 
+0200
@@ -164,6 +164,11 @@
                debug( "fatal error while ssl wrapping:", err )
                return false
        end
+
+       if self.conn.sni and self.servername then
+               self.conn:sni(self.servername);
+       end
+
        self.conn:settimeout( 0 )  -- set non blocking
        local handshakecallback = coroutine_wrap(function( event )
                local _, err
@@ -456,7 +461,7 @@
 
 -- End of client interface methods
 
-local function handleclient( client, ip, port, server, pattern, listener, 
sslctx )  -- creates an client interface
+local function handleclient( client, ip, port, server, pattern, listener, 
sslctx, extra )  -- creates an client interface
        --vdebug("creating client interfacce...")
        local interface = {
                type = "client";
@@ -492,6 +497,8 @@
                _serverport = (server and server:port() or nil),
                _sslctx = sslctx; -- parameters
                _usingssl = false;  -- client is using ssl;
+               extra = extra;
+               servername = extra and extra.servername;
        }
        if not has_luasec then interface.starttls = false; end
        interface.id = tostring(interface):match("%x+$");
@@ -716,14 +723,14 @@
        return interface
 end
 
-local function wrapclient( client, ip, port, listeners, pattern, sslctx )
-       local interface = handleclient( client, ip, port, nil, pattern, 
listeners, sslctx )
+local function wrapclient( client, ip, port, listeners, pattern, sslctx, extra 
)
+       local interface = handleclient( client, ip, port, nil, pattern, 
listeners, sslctx, extra )
        interface:_start_connection(sslctx)
        return interface, client
        --function handleclient( client, ip, port, server, pattern, listener, 
_, sslctx )  -- creates an client interface
 end
 
-local function addclient( addr, serverport, listener, pattern, sslctx, typ )
+local function addclient( addr, serverport, listener, pattern, sslctx, typ, 
extra )
        if sslctx and not has_luasec then
                debug "need luasec, but not available"
                return nil, "luasec not found"
@@ -750,7 +757,7 @@
        local res, err = client:setpeername( addr, serverport )  -- connect
        if res or ( err == "timeout" ) then
                local ip, port = client:getsockname( )
-               local interface = wrapclient( client, ip, serverport, listener, 
pattern, sslctx )
+               local interface = wrapclient( client, ip, serverport, listener, 
pattern, sslctx, extra )
                debug( "new connection id:", interface.id )
                return interface, err
        else
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/net/server_select.lua 
new/prosody-0.11.7/net/server_select.lua
--- old/prosody-0.11.6/net/server_select.lua    2020-08-01 12:58:37.000000000 
+0200
+++ new/prosody-0.11.7/net/server_select.lua    2020-05-31 22:39:34.000000000 
+0200
@@ -264,7 +264,7 @@
        return handler
 end
 
-wrapconnection = function( server, listeners, socket, ip, serverport, 
clientport, pattern, sslctx ) -- this function wraps a client to a handler 
object
+wrapconnection = function( server, listeners, socket, ip, serverport, 
clientport, pattern, sslctx, extra ) -- this function wraps a client to a 
handler object
 
        if socket:getfd() >= _maxfd then
                out_error("server.lua: Disallowed FD number: "..socket:getfd()) 
-- PROTIP: Switch to libevent
@@ -314,6 +314,11 @@
 
        local handler = bufferqueue -- saves a table ^_^
 
+       handler.extra = extra
+       if extra then
+               handler.servername = extra.servername
+       end
+
        handler.dispatch = function( )
                return dispatch
        end
@@ -624,6 +629,10 @@
                                return nil, err -- fatal error
                        end
 
+                       if socket.sni and self.servername then
+                               socket:sni(self.servername);
+                       end
+
                        socket:settimeout( 0 )
 
                        -- add the new socket to our system
@@ -977,8 +986,8 @@
 
 --// EXPERIMENTAL //--
 
-local wrapclient = function( socket, ip, serverport, listeners, pattern, 
sslctx )
-       local handler, socket, err = wrapconnection( nil, listeners, socket, 
ip, serverport, "clientport", pattern, sslctx )
+local wrapclient = function( socket, ip, serverport, listeners, pattern, 
sslctx, extra )
+       local handler, socket, err = wrapconnection( nil, listeners, socket, 
ip, serverport, "clientport", pattern, sslctx, extra)
        if not handler then return nil, err end
        _socketlist[ socket ] = handler
        if not sslctx then
@@ -997,7 +1006,7 @@
        return handler, socket
 end
 
-local addclient = function( address, port, listeners, pattern, sslctx, typ )
+local addclient = function( address, port, listeners, pattern, sslctx, typ, 
extra )
        local err
        if type( listeners ) ~= "table" then
                err = "invalid listener table"
@@ -1034,7 +1043,7 @@
        client:settimeout( 0 )
        local ok, err = client:setpeername( address, port )
        if ok or err == "timeout" or err == "Operation already in progress" then
-               return wrapclient( client, address, port, listeners, pattern, 
sslctx )
+               return wrapclient( client, address, port, listeners, pattern, 
sslctx, extra )
        else
                return nil, err
        end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/net/websocket/frames.lua 
new/prosody-0.11.7/net/websocket/frames.lua
--- old/prosody-0.11.6/net/websocket/frames.lua 2020-08-01 12:58:37.000000000 
+0200
+++ new/prosody-0.11.7/net/websocket/frames.lua 2020-05-31 22:39:34.000000000 
+0200
@@ -16,11 +16,10 @@
 local bxor = bit.bxor;
 local lshift = bit.lshift;
 local rshift = bit.rshift;
+local unpack = table.unpack or unpack; -- luacheck: ignore 113
 
 local t_concat = table.concat;
-local s_byte = string.byte;
 local s_char= string.char;
-local s_sub = string.sub;
 local s_pack = string.pack; -- luacheck: ignore 143
 local s_unpack = string.unpack; -- luacheck: ignore 143
 
@@ -30,12 +29,12 @@
 end
 
 local function read_uint16be(str, pos)
-       local l1, l2 = s_byte(str, pos, pos+1);
+       local l1, l2 = str:byte(pos, pos+1);
        return l1*256 + l2;
 end
 -- FIXME: this may lose precision
 local function read_uint64be(str, pos)
-       local l1, l2, l3, l4, l5, l6, l7, l8 = s_byte(str, pos, pos+7);
+       local l1, l2, l3, l4, l5, l6, l7, l8 = str:byte(pos, pos+7);
        local h = lshift(l1, 24) + lshift(l2, 16) + lshift(l3, 8) + l4;
        local l = lshift(l5, 24) + lshift(l6, 16) + lshift(l7, 8) + l8;
        return h * 2^32 + l;
@@ -63,9 +62,15 @@
 
 if s_unpack then
        function read_uint16be(str, pos)
+               if type(str) ~= "string" then
+                       str, pos = str:sub(pos, pos+1), 1;
+               end
                return s_unpack(">I2", str, pos);
        end
        function read_uint64be(str, pos)
+               if type(str) ~= "string" then
+                       str, pos = str:sub(pos, pos+7), 1;
+               end
                return s_unpack(">I8", str, pos);
        end
 end
@@ -73,7 +78,7 @@
 local function parse_frame_header(frame)
        if #frame < 2 then return; end
 
-       local byte1, byte2 = s_byte(frame, 1, 2);
+       local byte1, byte2 = frame:byte(1, 2);
        local result = {
                FIN = band(byte1, 0x80) > 0;
                RSV1 = band(byte1, 0x40) > 0;
@@ -102,7 +107,7 @@
        end
 
        if result.MASK then
-               result.key = { s_byte(frame, length_bytes+3, length_bytes+6) };
+               result.key = { frame:byte(length_bytes+3, length_bytes+6) };
        end
 
        return result, header_length;
@@ -121,7 +126,7 @@
        for i = from, to do
                local key_index = counter%key_len + 1;
                counter = counter + 1;
-               data[counter] = s_char(bxor(key[key_index], s_byte(str, i)));
+               data[counter] = s_char(bxor(key[key_index], str:byte(i)));
        end
        return t_concat(data);
 end
@@ -136,7 +141,7 @@
 
 local function parse_frame(frame)
        local result, pos = parse_frame_header(frame);
-       if result == nil or #frame < (pos + result.length) then return; end
+       if result == nil or #frame < (pos + result.length) then return nil, 
nil, result; end
        result.data = parse_frame_body(frame, result, pos+1);
        return result, pos + result.length;
 end
@@ -189,7 +194,7 @@
        if #data >= 2 then
                code = read_uint16be(data, 1);
                if #data > 2 then
-                       message = s_sub(data, 3);
+                       message = data:sub(3);
                end
        end
        return code, message
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/plugins/mod_c2s.lua 
new/prosody-0.11.7/plugins/mod_c2s.lua
--- old/prosody-0.11.6/plugins/mod_c2s.lua      2020-08-01 12:58:37.000000000 
+0200
+++ new/prosody-0.11.7/plugins/mod_c2s.lua      2020-05-31 22:39:34.000000000 
+0200
@@ -26,6 +26,7 @@
 local c2s_timeout = module:get_option_number("c2s_timeout", 300);
 local stream_close_timeout = module:get_option_number("c2s_close_timeout", 5);
 local opt_keepalives = module:get_option_boolean("c2s_tcp_keepalives", 
module:get_option_boolean("tcp_keepalives", true));
+local stanza_size_limit = module:get_option_number("c2s_stanza_size_limit"); 
-- TODO come up with a sensible default (util.xmppstream defaults to 10M)
 
 local measure_connections = module:measure("connections", "amount");
 local measure_ipv6 = module:measure("ipv6", "amount");
@@ -262,7 +263,7 @@
 
        session.close = session_close;
 
-       local stream = new_xmpp_stream(session, stream_callbacks);
+       local stream = new_xmpp_stream(session, stream_callbacks, 
stanza_size_limit);
        session.stream = stream;
        session.notopen = true;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/plugins/mod_s2s/mod_s2s.lua 
new/prosody-0.11.7/plugins/mod_s2s/mod_s2s.lua
--- old/prosody-0.11.6/plugins/mod_s2s/mod_s2s.lua      2020-08-01 
12:58:37.000000000 +0200
+++ new/prosody-0.11.7/plugins/mod_s2s/mod_s2s.lua      2020-05-31 
22:39:34.000000000 +0200
@@ -37,6 +37,7 @@
 local secure_domains, insecure_domains =
        module:get_option_set("s2s_secure_domains", {})._items, 
module:get_option_set("s2s_insecure_domains", {})._items;
 local require_encryption = module:get_option_boolean("s2s_require_encryption", 
false);
+local stanza_size_limit = module:get_option_number("s2s_stanza_size_limit"); 
-- TODO come up with a sensible default (util.xmppstream defaults to 10M)
 
 local measure_connections = module:measure("connections", "amount");
 local measure_ipv6 = module:measure("ipv6", "amount");
@@ -550,7 +551,7 @@
 
 -- Session initialization logic shared by incoming and outgoing
 local function initialize_session(session)
-       local stream = new_xmpp_stream(session, stream_callbacks);
+       local stream = new_xmpp_stream(session, stream_callbacks, 
stanza_size_limit);
 
        session.thread = runner(function (stanza)
                if stanza.name == nil then
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/plugins/mod_websocket.lua 
new/prosody-0.11.7/plugins/mod_websocket.lua
--- old/prosody-0.11.6/plugins/mod_websocket.lua        2020-08-01 
12:58:37.000000000 +0200
+++ new/prosody-0.11.7/plugins/mod_websocket.lua        2020-05-31 
22:39:34.000000000 +0200
@@ -18,6 +18,7 @@
 local portmanager = require "core.portmanager";
 local sm_destroy_session = require"core.sessionmanager".destroy_session;
 local log = module._log;
+local dbuffer = require "util.dbuffer";
 
 local websocket_frames = require"net.websocket.frames";
 local parse_frame = websocket_frames.parse;
@@ -27,6 +28,9 @@
 
 local t_concat = table.concat;
 
+local stanza_size_limit = module:get_option_number("c2s_stanza_size_limit", 10 
* 1024 * 1024);
+local frame_buffer_limit = 
module:get_option_number("websocket_frame_buffer_limit", 2 * stanza_size_limit);
+local frame_fragment_limit = 
module:get_option_number("websocket_frame_fragment_limit", 8);
 local stream_close_timeout = module:get_option_number("c2s_close_timeout", 5);
 local consider_websocket_secure = 
module:get_option_boolean("consider_websocket_secure");
 local cross_domain = module:get_option_set("cross_domain_websocket", {});
@@ -138,6 +142,65 @@
 
        return data;
 end
+
+local function validate_frame(frame, max_length)
+       local opcode, length = frame.opcode, frame.length;
+
+       if max_length and length > max_length then
+               return false, 1009, "Payload too large";
+       end
+
+       -- Error cases
+       if frame.RSV1 or frame.RSV2 or frame.RSV3 then -- Reserved bits non zero
+               return false, 1002, "Reserved bits not zero";
+       end
+
+       if opcode == 0x8 and frame.data then -- close frame
+               if length == 1 then
+                       return false, 1002, "Close frame with payload, but too 
short for status code";
+               elseif length >= 2 then
+                       local status_code = parse_close(frame.data)
+                       if status_code < 1000 then
+                               return false, 1002, "Closed with invalid status 
code";
+                       elseif ((status_code > 1003 and status_code < 1007) or 
status_code > 1011) and status_code < 3000 then
+                               return false, 1002, "Closed with reserved 
status code";
+                       end
+               end
+       end
+
+       if opcode >= 0x8 then
+               if length > 125 then -- Control frame with too much payload
+                       return false, 1002, "Payload too large";
+               end
+
+               if not frame.FIN then -- Fragmented control frame
+                       return false, 1002, "Fragmented control frame";
+               end
+       end
+
+       if (opcode > 0x2 and opcode < 0x8) or (opcode > 0xA) then
+               return false, 1002, "Reserved opcode";
+       end
+
+       -- Check opcode
+       if opcode == 0x2 then -- Binary frame
+               return false, 1003, "Only text frames are supported, RFC 7395 
3.2";
+       elseif opcode == 0x8 then -- Close request
+               return false, 1000, "Goodbye";
+       end
+
+       -- Other (XMPP-specific) validity checks
+       if not frame.FIN then
+               return false, 1003, "Continuation frames are not supported, RFC 
7395 3.3.3";
+       end
+       if opcode == 0x01 and frame.data and frame.data:byte(1, 1) ~= 60 then
+               return false, 1007, "Invalid payload start character, RFC 7395 
3.3.3";
+       end
+
+       return true;
+end
+
+
 function handle_request(event)
        local request, response = event.request, event.response;
        local conn = response.conn;
@@ -168,90 +231,40 @@
                conn:close();
        end
 
-       local dataBuffer;
-       local function handle_frame(frame)
-               local opcode = frame.opcode;
-               local length = frame.length;
-               module:log("debug", "Websocket received frame: opcode=%0x, %i 
bytes", frame.opcode, #frame.data);
-
-               -- Error cases
-               if frame.RSV1 or frame.RSV2 or frame.RSV3 then -- Reserved bits 
non zero
-                       websocket_close(1002, "Reserved bits not zero");
-                       return false;
-               end
-
-               if opcode == 0x8 then -- close frame
-                       if length == 1 then
-                               websocket_close(1002, "Close frame with 
payload, but too short for status code");
-                               return false;
-                       elseif length >= 2 then
-                               local status_code = parse_close(frame.data)
-                               if status_code < 1000 then
-                                       websocket_close(1002, "Closed with 
invalid status code");
-                                       return false;
-                               elseif ((status_code > 1003 and status_code < 
1007) or status_code > 1011) and status_code < 3000 then
-                                       websocket_close(1002, "Closed with 
reserved status code");
-                                       return false;
-                               end
-                       end
-               end
-
-               if opcode >= 0x8 then
-                       if length > 125 then -- Control frame with too much 
payload
-                               websocket_close(1002, "Payload too large");
-                               return false;
-                       end
-
-                       if not frame.FIN then -- Fragmented control frame
-                               websocket_close(1002, "Fragmented control 
frame");
-                               return false;
-                       end
-               end
-
-               if (opcode > 0x2 and opcode < 0x8) or (opcode > 0xA) then
-                       websocket_close(1002, "Reserved opcode");
-                       return false;
+       local function websocket_handle_error(session, code, message)
+               if code == 1009 then -- stanza size limit exceeded
+                       -- we close the session, rather than the connection,
+                       -- otherwise a resuming client will simply resend the
+                       -- offending stanza
+                       session:close({ condition = "policy-violation", text = 
"stanza too large" });
+               else
+                       websocket_close(code, message);
                end
+       end
 
-               if opcode == 0x0 and not dataBuffer then
-                       websocket_close(1002, "Unexpected continuation frame");
-                       return false;
-               end
+       local function handle_frame(frame)
+               module:log("debug", "Websocket received frame: opcode=%0x, %i 
bytes", frame.opcode, #frame.data);
 
-               if (opcode == 0x1 or opcode == 0x2) and dataBuffer then
-                       websocket_close(1002, "Continuation frame expected");
-                       return false;
+               -- Check frame makes sense
+               local frame_ok, err_status, err_text = validate_frame(frame, 
stanza_size_limit);
+               if not frame_ok then
+                       return frame_ok, err_status, err_text;
                end
 
-               -- Valid cases
-               if opcode == 0x0 then -- Continuation frame
-                       dataBuffer[#dataBuffer+1] = frame.data;
-               elseif opcode == 0x1 then -- Text frame
-                       dataBuffer = {frame.data};
-               elseif opcode == 0x2 then -- Binary frame
-                       websocket_close(1003, "Only text frames are supported");
-                       return;
-               elseif opcode == 0x8 then -- Close request
-                       websocket_close(1000, "Goodbye");
-                       return;
-               elseif opcode == 0x9 then -- Ping frame
+               local opcode = frame.opcode;
+               if opcode == 0x9 then -- Ping frame
                        frame.opcode = 0xA;
                        frame.MASK = false; -- Clients send masked frames, 
servers don't, see #1484
                        conn:write(build_frame(frame));
                        return "";
                elseif opcode == 0xA then -- Pong frame, MAY be sent 
unsolicited, eg as keepalive
                        return "";
-               else
+               elseif opcode ~= 0x1 then -- Not text frame (which is all we 
support)
                        log("warn", "Received frame with unsupported opcode 
%i", opcode);
                        return "";
                end
 
-               if frame.FIN then
-                       local data = t_concat(dataBuffer, "");
-                       dataBuffer = nil;
-                       return data;
-               end
-               return "";
+               return frame.data;
        end
 
        conn:setlistener(c2s_listener);
@@ -269,19 +282,37 @@
        session.open_stream = session_open_stream;
        session.close = session_close;
 
-       local frameBuffer = "";
+       local frameBuffer = dbuffer.new(frame_buffer_limit, 
frame_fragment_limit);
        add_filter(session, "bytes/in", function(data)
+               if not frameBuffer:write(data) then
+                       session.log("warn", "websocket frame buffer full - 
terminating session");
+                       session:close({ condition = "resource-constraint", text 
= "frame buffer exceeded" });
+                       return;
+               end
+
                local cache = {};
-               frameBuffer = frameBuffer .. data;
-               local frame, length = parse_frame(frameBuffer);
+               local frame, length, partial = parse_frame(frameBuffer);
 
                while frame do
-                       frameBuffer = frameBuffer:sub(length + 1);
-                       local result = handle_frame(frame);
-                       if not result then return; end
+                       frameBuffer:discard(length);
+                       local result, err_status, err_text = 
handle_frame(frame);
+                       if not result then
+                               websocket_handle_error(session, err_status, 
err_text);
+                               break;
+                       end
                        cache[#cache+1] = filter_open_close(result);
-                       frame, length = parse_frame(frameBuffer);
+                       frame, length, partial = parse_frame(frameBuffer);
                end
+
+               if partial then
+                       -- The header of the next frame is already in the 
buffer, run
+                       -- some early validation here
+                       local frame_ok, err_status, err_text = 
validate_frame(partial, stanza_size_limit);
+                       if not frame_ok then
+                               websocket_handle_error(session, err_status, 
err_text);
+                       end
+               end
+
                return t_concat(cache, "");
        end);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/prosody.release 
new/prosody-0.11.7/prosody.release
--- old/prosody-0.11.6/prosody.release  2020-09-09 11:24:29.000000000 +0200
+++ new/prosody-0.11.7/prosody.release  2020-10-01 16:16:52.000000000 +0200
@@ -1 +1 @@
-0.11.6
+0.11.7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/spec/util_dbuffer_spec.lua 
new/prosody-0.11.7/spec/util_dbuffer_spec.lua
--- old/prosody-0.11.6/spec/util_dbuffer_spec.lua       1970-01-01 
01:00:00.000000000 +0100
+++ new/prosody-0.11.7/spec/util_dbuffer_spec.lua       2020-05-31 
22:39:34.000000000 +0200
@@ -0,0 +1,130 @@
+local dbuffer = require "util.dbuffer";
+describe("util.dbuffer", function ()
+       describe("#new", function ()
+               it("has a constructor", function ()
+                       assert.Function(dbuffer.new);
+               end);
+               it("can be created", function ()
+                       assert.truthy(dbuffer.new());
+               end);
+               it("won't create an empty buffer", function ()
+                       assert.falsy(dbuffer.new(0));
+               end);
+               it("won't create a negatively sized buffer", function ()
+                       assert.falsy(dbuffer.new(-1));
+               end);
+       end);
+       describe(":write", function ()
+               local b = dbuffer.new();
+               it("works", function ()
+                       assert.truthy(b:write("hi"));
+               end);
+       end);
+
+       describe(":read", function ()
+               it("supports optional bytes parameter", function ()
+                       -- should return the frontmost chunk
+                       local b = dbuffer.new();
+                       assert.truthy(b:write("hello"));
+                       assert.truthy(b:write(" "));
+                       assert.truthy(b:write("world"));
+                       assert.equal("h", b:read(1));
+
+                       assert.equal("ello", b:read());
+                       assert.equal(" ", b:read());
+                       assert.equal("world", b:read());
+               end);
+       end);
+
+       describe(":discard", function ()
+               local b = dbuffer.new();
+               it("works", function ()
+                       assert.truthy(b:write("hello world"));
+                       assert.truthy(b:discard(6));
+                       assert.equal(5, b:length());
+                       assert.equal("world", b:read(5));
+               end);
+       end);
+
+       describe(":collapse()", function ()
+               it("works on an empty buffer", function ()
+                       local b = dbuffer.new();
+                       b:collapse();
+               end);
+       end);
+
+       describe(":sub", function ()
+               -- Helper function to compare buffer:sub() with string:sub()
+               local s = "hello world";
+               local function test_sub(b, x, y)
+                       local string_result, buffer_result = s:sub(x, y), 
b:sub(x, y);
+                       assert.equals(string_result, buffer_result, 
("buffer:sub(%d, %s) does not match string:sub()"):format(x, y and 
("%d"):format(y) or "nil"));
+               end
+
+               it("works", function ()
+                       local b = dbuffer.new();
+                       assert.truthy(b:write("hello world"));
+                       assert.equals("hello", b:sub(1, 5));
+               end);
+
+               it("works after discard", function ()
+                       local b = dbuffer.new(256);
+                       assert.truthy(b:write("foobar"));
+                       assert.equals("foobar", b:sub(1, 6));
+                       assert.truthy(b:discard(3)); -- consume "foo"
+                       assert.equals("bar", b:sub(1, 3));
+               end);
+
+               it("supports optional end parameter", function ()
+                       local b = dbuffer.new();
+                       assert.truthy(b:write("hello world"));
+                       assert.equals("hello world", b:sub(1));
+                       assert.equals("world", b:sub(-5));
+               end);
+
+               it("is equivalent to string:sub", function ()
+                       local b = dbuffer.new(11);
+                       assert.truthy(b:write(s));
+                       for i = -13, 13 do
+                               for j = -13, 13 do
+                                       test_sub(b, i, j);
+                               end
+                       end
+               end);
+       end);
+
+       describe(":byte", function ()
+               -- Helper function to compare buffer:byte() with string:byte()
+               local s = "hello world"
+               local function test_byte(b, x, y)
+                       local string_result, buffer_result = {s:byte(x, y)}, 
{b:byte(x, y)};
+                       assert.same(string_result, buffer_result, 
("buffer:byte(%d, %s) does not match string:byte()"):format(x, y and 
("%d"):format(y) or "nil"));
+               end
+
+               it("is equivalent to string:byte", function ()
+                       local b = dbuffer.new(11);
+                       assert.truthy(b:write(s));
+                       test_byte(b, 1);
+                       test_byte(b, 3);
+                       test_byte(b, -1);
+                       test_byte(b, -3);
+                       for i = -13, 13 do
+                               for j = -13, 13 do
+                                       test_byte(b, i, j);
+                               end
+                       end
+               end);
+
+               it("works with characters > 127", function ()
+                       local b = dbuffer.new();
+                       b:write(string.char(0, 140));
+                       local r = { b:byte(1, 2) };
+                       assert.same({ 0, 140 }, r);
+               end);
+
+               it("works on an empty buffer", function ()
+                       local b = dbuffer.new();
+                       assert.equal("", b:sub(1,1));
+               end);
+       end);
+end);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/spec/util_indexedbheap_spec.lua 
new/prosody-0.11.7/spec/util_indexedbheap_spec.lua
--- old/prosody-0.11.6/spec/util_indexedbheap_spec.lua  1970-01-01 
01:00:00.000000000 +0100
+++ new/prosody-0.11.7/spec/util_indexedbheap_spec.lua  2020-05-31 
22:39:34.000000000 +0200
@@ -0,0 +1,33 @@
+local ibh = require"util.indexedbheap";
+
+local function verify_heap_property(priorities)
+       for k in ipairs(priorities) do
+               local parent = priorities[k];
+               local childA = priorities[2*k];
+               local childB = priorities[2*k+1];
+               -- print("-", parent, childA, childB)
+               assert(childA == nil or childA > parent, "heap property 
violated");
+               assert(childB == nil or childB > parent, "heap property 
violated");
+       end
+end
+
+local h
+setup(function ()
+       h = ibh.create();
+end)
+describe("util.indexedbheap", function ()
+       it("item can be moved from end to top", function ()
+               verify_heap_property(h);
+               h:insert("a", 1);
+               verify_heap_property(h);
+               h:insert("b", 2);
+               verify_heap_property(h);
+               h:insert("c", 3);
+               verify_heap_property(h);
+               local id = h:insert("*", 10);
+               verify_heap_property(h);
+               h:reprioritize(id, 0);
+               verify_heap_property(h);
+               assert.same({ 0, "*", id }, { h:pop() });
+       end)
+end);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/util/dbuffer.lua 
new/prosody-0.11.7/util/dbuffer.lua
--- old/prosody-0.11.6/util/dbuffer.lua 1970-01-01 01:00:00.000000000 +0100
+++ new/prosody-0.11.7/util/dbuffer.lua 2020-05-31 22:39:34.000000000 +0200
@@ -0,0 +1,176 @@
+local queue = require "util.queue";
+
+local dbuffer_methods = {};
+local dynamic_buffer_mt = { __index = dbuffer_methods };
+
+function dbuffer_methods:write(data)
+       if self.max_size and #data + self._length > self.max_size then
+               return nil;
+       end
+       local ok = self.items:push(data);
+       if not ok then
+               self:collapse();
+               ok = self.items:push(data);
+       end
+       if not ok then
+               return nil;
+       end
+       self._length = self._length + #data;
+       return true;
+end
+
+function dbuffer_methods:read_chunk(requested_bytes)
+       local chunk, consumed = self.items:peek(), self.front_consumed;
+       if not chunk then return; end
+       local chunk_length = #chunk;
+       local remaining_chunk_length = chunk_length - consumed;
+       if not requested_bytes then
+               requested_bytes = remaining_chunk_length;
+       end
+       if remaining_chunk_length <= requested_bytes then
+               self.front_consumed = 0;
+               self._length = self._length - remaining_chunk_length;
+               self.items:pop();
+               assert(#chunk:sub(consumed + 1, -1) == remaining_chunk_length);
+               return chunk:sub(consumed + 1, -1), remaining_chunk_length;
+       end
+       local end_pos = consumed + requested_bytes;
+       self.front_consumed = end_pos;
+       self._length = self._length - requested_bytes;
+       assert(#chunk:sub(consumed + 1, end_pos) == requested_bytes);
+       return chunk:sub(consumed + 1, end_pos), requested_bytes;
+end
+
+function dbuffer_methods:read(requested_bytes)
+       local chunks;
+
+       if requested_bytes and requested_bytes > self._length then
+               return nil;
+       end
+
+       local chunk, read_bytes = self:read_chunk(requested_bytes);
+       if not requested_bytes then
+               return chunk;
+       elseif chunk then
+               requested_bytes = requested_bytes - read_bytes;
+               if requested_bytes == 0 then -- Already read everything we need
+                       return chunk;
+               end
+               chunks = {};
+       else
+               return nil;
+       end
+
+       -- Need to keep reading more chunks
+       while chunk do
+               table.insert(chunks, chunk);
+               if requested_bytes > 0 then
+                       chunk, read_bytes = self:read_chunk(requested_bytes);
+                       requested_bytes = requested_bytes - read_bytes;
+               else
+                       break;
+               end
+       end
+
+       return table.concat(chunks);
+end
+
+function dbuffer_methods:discard(requested_bytes)
+       if requested_bytes > self._length then
+               return nil;
+       end
+
+       local chunk, read_bytes = self:read_chunk(requested_bytes);
+       if chunk then
+               requested_bytes = requested_bytes - read_bytes;
+               if requested_bytes == 0 then -- Already read everything we need
+                       return true;
+               end
+       else
+               return nil;
+       end
+
+       while chunk do
+               if requested_bytes > 0 then
+                       chunk, read_bytes = self:read_chunk(requested_bytes);
+                       requested_bytes = requested_bytes - read_bytes;
+               else
+                       break;
+               end
+       end
+       return true;
+end
+
+function dbuffer_methods:sub(i, j)
+       if j == nil then
+               j = -1;
+       end
+       if j < 0 then
+               j = self._length + (j+1);
+       end
+       if i < 0 then
+               i = self._length + (i+1);
+       end
+       if i < 1 then
+               i = 1;
+       end
+       if j > self._length then
+               j = self._length;
+       end
+       if i > j then
+               return "";
+       end
+
+       self:collapse(j);
+
+       return self.items:peek():sub(self.front_consumed+1):sub(i, j);
+end
+
+function dbuffer_methods:byte(i, j)
+       i = i or 1;
+       j = j or i;
+       return string.byte(self:sub(i, j), 1, -1);
+end
+
+function dbuffer_methods:length()
+       return self._length;
+end
+dynamic_buffer_mt.__len = dbuffer_methods.length; -- support # operator
+
+function dbuffer_methods:collapse(bytes)
+       bytes = bytes or self._length;
+
+       local front_chunk = self.items:peek();
+
+       if not front_chunk or #front_chunk - self.front_consumed >= bytes then
+               return;
+       end
+
+       local front_chunks = { front_chunk:sub(self.front_consumed+1) };
+       local front_bytes = #front_chunks[1];
+
+       while front_bytes < bytes do
+               self.items:pop();
+               local chunk = self.items:peek();
+               front_bytes = front_bytes + #chunk;
+               table.insert(front_chunks, chunk);
+       end
+       self.items:replace(table.concat(front_chunks));
+       self.front_consumed = 0;
+end
+
+local function new(max_size, max_chunks)
+       if max_size and max_size <= 0 then
+               return nil;
+       end
+       return setmetatable({
+               front_consumed = 0;
+               _length = 0;
+               max_size = max_size;
+               items = queue.new(max_chunks or 32);
+       }, dynamic_buffer_mt);
+end
+
+return {
+       new = new;
+};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/util/gc.lua 
new/prosody-0.11.7/util/gc.lua
--- old/prosody-0.11.6/util/gc.lua      1970-01-01 01:00:00.000000000 +0100
+++ new/prosody-0.11.7/util/gc.lua      2020-05-31 22:39:34.000000000 +0200
@@ -0,0 +1,49 @@
+local set = require "util.set";
+
+local known_options = {
+       incremental = set.new { "mode", "threshold", "speed", "step_size" };
+       generational = set.new { "mode", "minor_threshold", "major_threshold" };
+};
+
+if _VERSION ~= "5.4" then
+       known_options.generational = nil;
+       known_options.incremental:remove("step_size");
+end
+
+local function configure(user, defaults)
+       local mode = user.mode or defaults.mode or "incremental";
+       if not known_options[mode] then
+               return nil, "GC mode not supported on ".._VERSION..": "..mode;
+       end
+
+       for k, v in pairs(user) do
+               if not known_options[mode]:contains(k) then
+                       return nil, "Unknown GC parameter: "..k;
+               elseif k ~= "mode" and type(v) ~= "number" then
+                       return nil, "parameter '"..k.."' should be a number";
+               end
+       end
+
+       if mode == "incremental" then
+               if _VERSION == "Lua 5.4" then
+                       collectgarbage(mode,
+                               user.threshold or defaults.threshold,
+                               user.speed or defaults.speed,
+                               user.step_size or defaults.step_size
+                       );
+               else
+                       collectgarbage("setpause", user.threshold or 
defaults.threshold);
+                       collectgarbage("setstepmul", user.speed or 
defaults.speed);
+               end
+       elseif mode == "generational" then
+               collectgarbage(mode,
+                       user.minor_threshold or defaults.minor_threshold,
+                       user.major_threshold or defaults.major_threshold
+               );
+       end
+       return true;
+end
+
+return {
+       configure = configure;
+};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/util/indexedbheap.lua 
new/prosody-0.11.7/util/indexedbheap.lua
--- old/prosody-0.11.6/util/indexedbheap.lua    2020-08-01 12:58:37.000000000 
+0200
+++ new/prosody-0.11.7/util/indexedbheap.lua    2020-05-31 22:39:34.000000000 
+0200
@@ -23,7 +23,7 @@
        local tmp_sync = sync[k];
        while k ~= 1 do
                local parent = math_floor(k/2);
-               if tmp < self[parent] then break; end
+               if tmp >= self[parent] then break; end
                self[k] = self[parent];
                sync[k] = sync[parent];
                index[sync[k]] = k;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/util/queue.lua 
new/prosody-0.11.7/util/queue.lua
--- old/prosody-0.11.6/util/queue.lua   2020-08-01 12:58:37.000000000 +0200
+++ new/prosody-0.11.7/util/queue.lua   2020-05-31 22:39:34.000000000 +0200
@@ -51,6 +51,13 @@
                        end
                        return t[tail];
                end;
+               replace = function (self, data)
+                       if items == 0 then
+                               return self:push(data);
+                       end
+                       t[tail] = data;
+                       return true;
+               end;
                items = function (self)
                        --luacheck: ignore 431/t
                        return function (t, pos)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/prosody-0.11.6/util/startup.lua 
new/prosody-0.11.7/util/startup.lua
--- old/prosody-0.11.6/util/startup.lua 2020-08-01 12:58:37.000000000 +0200
+++ new/prosody-0.11.7/util/startup.lua 2020-05-31 22:39:34.000000000 +0200
@@ -12,6 +12,8 @@
 
 local original_logging_config;
 
+local default_gc_params = { mode = "incremental", threshold = 105, speed = 250 
};
+
 local short_params = { D = "daemonize", F = "no-daemonize" };
 local value_params = { config = true };
 
@@ -544,6 +546,19 @@
        end
 end
 
+function startup.init_gc()
+       -- Apply garbage collector settings from the config file
+       local gc = require "util.gc";
+       local gc_settings = config.get("*", "gc") or { mode = 
default_gc_params.mode };
+
+       local ok, err = gc.configure(gc_settings, default_gc_params);
+       if not ok then
+               log("error", "Failed to apply GC configuration: %s", err);
+               return nil, err;
+       end
+       return true;
+end
+
 function startup.make_host(hostname)
        return {
                type = "local",
@@ -573,6 +588,7 @@
        startup.read_config();
        startup.force_console_logging();
        startup.init_logging();
+       startup.init_gc();
        startup.setup_plugindir();
        startup.setup_datadir();
        startup.chdir();
@@ -593,6 +609,7 @@
        startup.init_global_state();
        startup.read_config();
        startup.init_logging();
+       startup.init_gc();
        startup.sanity_check();
        startup.sandbox_require();
        startup.set_function_metatable();



Reply via email to