From d4c35255c06539fdc928b3b12df61f2ebd09ad64 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 28 Mar 2019 12:52:55 +0100 Subject: net.server_epoll: Skip delayed continuation read on paused connections This should prevent #1333 in cases where LuaSockets buffer is "dirty", i.e.?contains more data after a read, where it gets resumed with a short delay. --- net/server_epoll.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/server_epoll.lua b/net/server_epoll.lua index cffd3a84..c41266e1 100644 --- a/net/server_epoll.lua +++ b/net/server_epoll.lua @@ -373,7 +373,7 @@ function interface:onreadable() end end if not self.conn then return; end - if self.conn:dirty() then + if self._wantread and self.conn:dirty() then self:setreadtimeout(false); self:pausefor(cfg.read_retry_delay); else -- cgit v1.2.3 From dadf242556770176ea589426bf97444a550c4553 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 13 Apr 2019 18:16:28 +0200 Subject: net.dns: Close resolv.conf handle when done (fixes #1342) --- net/dns.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/net/dns.lua b/net/dns.lua index af5f1216..3902f95c 100644 --- a/net/dns.lua +++ b/net/dns.lua @@ -704,6 +704,7 @@ function resolver:adddefaultnameservers() -- - - - - adddefaultnameservers end end end + resolv_conf:close(); end if not self.server or #self.server == 0 then -- TODO log warning about no nameservers, adding localhost as the default nameserver -- cgit v1.2.3 From 85355a78c23909937f9511c7401a4119e2b6e25d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 14 Apr 2019 02:06:20 +0200 Subject: util.ip: Add missing netmask for 192.168/16 range (fixes #1343) --- util/ip.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/ip.lua b/util/ip.lua index 0ec9e297..d1808225 100644 --- a/util/ip.lua +++ b/util/ip.lua @@ -203,7 +203,7 @@ local rfc6598 = new_ip("100.64.0.0"); function ip_methods:private() local private = self.scope ~= 0xE; if not private and self.proto == "IPv4" then - return match(self, rfc1918_8, 8) or match(self, rfc1918_12, 12) or match(self, rfc1918_16) or match(self, rfc6598, 10); + return match(self, rfc1918_8, 8) or match(self, rfc1918_12, 12) or match(self, rfc1918_16, 16) or match(self, rfc6598, 10); end return private; end -- cgit v1.2.3 From b84ddeeb2fa944cf424c30ccd0fea77a797cce8c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 19 Apr 2019 13:12:00 +0200 Subject: util.hashes: Use HMAC function provided by OpenSSL (fixes #1345) --- util-src/hashes.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/util-src/hashes.c b/util-src/hashes.c index 82f5876e..903ecb6e 100644 --- a/util-src/hashes.c +++ b/util-src/hashes.c @@ -25,6 +25,7 @@ typedef unsigned __int32 uint32_t; #include "lauxlib.h" #include #include +#include #if (LUA_VERSION_NUM == 501) #define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R) @@ -112,35 +113,28 @@ static void hmac(struct hash_desc *desc, const char *key, size_t key_len, desc->Final(result, desc->ctxo); } -#define MAKE_HMAC_FUNCTION(myFunc, func, size, type) \ +#define MAKE_HMAC_FUNCTION(myFunc, evp, size, type) \ static int myFunc(lua_State *L) { \ - type ctx, ctxo; \ unsigned char hash[size], result[2*size]; \ size_t key_len, msg_len; \ + unsigned int out_len; \ const char *key = luaL_checklstring(L, 1, &key_len); \ const char *msg = luaL_checklstring(L, 2, &msg_len); \ const int hex_out = lua_toboolean(L, 3); \ - struct hash_desc desc; \ - desc.Init = (int (*)(void*))func##_Init; \ - desc.Update = (int (*)(void*, const void *, size_t))func##_Update; \ - desc.Final = (int (*)(unsigned char*, void*))func##_Final; \ - desc.digestLength = size; \ - desc.ctx = &ctx; \ - desc.ctxo = &ctxo; \ - hmac(&desc, key, key_len, msg, msg_len, hash); \ + HMAC(evp(), key, key_len, (const unsigned char*)msg, msg_len, (unsigned char*)hash, &out_len); \ if (hex_out) { \ - toHex(hash, size, result); \ - lua_pushlstring(L, (char*)result, size*2); \ + toHex(hash, out_len, result); \ + lua_pushlstring(L, (char*)result, out_len*2); \ } else { \ - lua_pushlstring(L, (char*)hash, size); \ + lua_pushlstring(L, (char*)hash, out_len); \ } \ return 1; \ } -MAKE_HMAC_FUNCTION(Lhmac_sha1, SHA1, SHA_DIGEST_LENGTH, SHA_CTX) -MAKE_HMAC_FUNCTION(Lhmac_sha256, SHA256, SHA256_DIGEST_LENGTH, SHA256_CTX) -MAKE_HMAC_FUNCTION(Lhmac_sha512, SHA512, SHA512_DIGEST_LENGTH, SHA512_CTX) -MAKE_HMAC_FUNCTION(Lhmac_md5, MD5, MD5_DIGEST_LENGTH, MD5_CTX) +MAKE_HMAC_FUNCTION(Lhmac_sha1, EVP_sha1, SHA_DIGEST_LENGTH, SHA_CTX) +MAKE_HMAC_FUNCTION(Lhmac_sha256, EVP_sha256, SHA256_DIGEST_LENGTH, SHA256_CTX) +MAKE_HMAC_FUNCTION(Lhmac_sha512, EVP_sha512, SHA512_DIGEST_LENGTH, SHA512_CTX) +MAKE_HMAC_FUNCTION(Lhmac_md5, EVP_md5, MD5_DIGEST_LENGTH, MD5_CTX) static int LscramHi(lua_State *L) { union xory { -- cgit v1.2.3 From 4d9fdf2b0fd2213a17bf6bc223617561cea66902 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 23 Apr 2019 19:13:50 +0200 Subject: mod_tls: Log debug message for each kind of TLS context created Creating TLS contexts triggers a lot of messages from certmanager that don't really describe their purpose. This is meant to provide hints about that. --- plugins/mod_tls.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/mod_tls.lua b/plugins/mod_tls.lua index 029ddd1d..eb208e28 100644 --- a/plugins/mod_tls.lua +++ b/plugins/mod_tls.lua @@ -52,12 +52,15 @@ function module.load() local parent_s2s = rawgetopt(parent, "s2s_ssl") or NULL; local host_s2s = rawgetopt(modhost, "s2s_ssl") or parent_s2s; + module:log("debug", "Creating context for c2s"); ssl_ctx_c2s, err, ssl_cfg_c2s = create_context(host.host, "server", host_c2s, host_ssl, global_c2s); -- for incoming client connections if not ssl_ctx_c2s then module:log("error", "Error creating context for c2s: %s", err); end + module:log("debug", "Creating context for s2sout"); ssl_ctx_s2sout, err, ssl_cfg_s2sout = create_context(host.host, "client", host_s2s, host_ssl, global_s2s); -- for outgoing server connections if not ssl_ctx_s2sout then module:log("error", "Error creating contexts for s2sout: %s", err); end + module:log("debug", "Creating context for s2sin"); ssl_ctx_s2sin, err, ssl_cfg_s2sin = create_context(host.host, "server", host_s2s, host_ssl, global_s2s); -- for incoming server connections if not ssl_ctx_s2sin then module:log("error", "Error creating contexts for s2sin: %s", err); end end -- cgit v1.2.3 From 7c01e612b7e93c1666e63ab33c0340b5b33d076b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 23 Apr 2019 19:21:06 +0200 Subject: core.portmanager: Log debug message for each kind of TLS context created --- core/portmanager.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/core/portmanager.lua b/core/portmanager.lua index 1ed37da0..bed5eca5 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -112,6 +112,7 @@ local function activate(service_name) if service_info.encryption == "ssl" then local global_ssl_config = config.get("*", "ssl") or {}; local prefix_ssl_config = config.get("*", config_prefix.."ssl") or global_ssl_config; + log("debug", "Creating context for direct TLS service %s on port %d", service_info.name, port); ssl, err = certmanager.create_context(service_info.name.." port "..port, "server", prefix_ssl_config[interface], prefix_ssl_config[port], -- cgit v1.2.3 From 4ff75d0fc9c98e1b25c4f9c72060d14b15a0dc9c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 24 Apr 2019 16:16:19 +0200 Subject: util.encodings: Allow unassigned code points in ICU mode to match libidn behavior (fixes #1348) --- util-src/encodings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util-src/encodings.c b/util-src/encodings.c index f7e8131f..e55a3f44 100644 --- a/util-src/encodings.c +++ b/util-src/encodings.c @@ -299,7 +299,7 @@ static int icu_stringprep_prep(lua_State *L, const UStringPrepProfile *profile) return 1; } - prepped_len = usprep_prepare(profile, unprepped, unprepped_len, prepped, 1024, 0, NULL, &err); + prepped_len = usprep_prepare(profile, unprepped, unprepped_len, prepped, 1024, USPREP_ALLOW_UNASSIGNED, NULL, &err); if(U_FAILURE(err)) { lua_pushnil(L); -- cgit v1.2.3 From b00d2764e71e9f85021e21f0b74790a9212bebfd Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 1 May 2019 22:34:15 +0200 Subject: prosodyctl: Include version of LuaDBI in 'about' --- prosodyctl | 1 + 1 file changed, 1 insertion(+) diff --git a/prosodyctl b/prosodyctl index b1c6fffd..e580446b 100755 --- a/prosodyctl +++ b/prosodyctl @@ -395,6 +395,7 @@ function commands.about(arg) local module_versions, longest_name = {}, 8; local luaevent =dependencies.softreq"luaevent"; dependencies.softreq"ssl"; + dependencies.softreq"DBI"; for name, module in pairs(package.loaded) do if type(module) == "table" and rawget(module, "_VERSION") and name ~= "_G" and not name:match("%.") then -- cgit v1.2.3 From 36957928e1a7911c6dd19e58020c85512b47795d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 4 May 2019 04:23:35 +0200 Subject: net.server_epoll: Restore wantread flag after pause (fixes #1354) If a chunk of data has been received that is larger than the amount read at a time, then the connection is paused for a short time after which it tries to read some more. If, after that, there is still more data to read, it should do the same thing. However, because the "want read" flag is removed and was restored after the delayed reading, it would not schedule another delayed read. --- net/server_epoll.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/server_epoll.lua b/net/server_epoll.lua index c41266e1..c279c579 100644 --- a/net/server_epoll.lua +++ b/net/server_epoll.lua @@ -600,10 +600,10 @@ function interface:pausefor(t) self:set(false); self._pausefor = addtimer(t, function () self._pausefor = nil; + self:set(true); if self.conn and self.conn:dirty() then self:onreadable(); end - self:set(true); end); end -- cgit v1.2.3 From 8fd66538f60d02b3778f1037dce9bda864874476 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 4 May 2019 22:48:59 +0200 Subject: configure: Handle lua being found in /bin (workaround for #1353) --- configure | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/configure b/configure index 4307997c..0fe186b5 100755 --- a/configure +++ b/configure @@ -419,6 +419,13 @@ else } fi +# See #1353 +if [ "$LUA_DIR_SET" != "yes" ] && [ "$LUA_DIR" = "/" ] +then + LUA_DIR="/usr" +fi + + if [ "$lua_interp_found" != "yes" ] && [ "$RUNWITH_SET" != "yes" ] then if [ "$LUA_VERSION_SET" ]; then -- cgit v1.2.3 From 1045642fa450a0dd46009a39c6a1fa4b949e9888 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 13 May 2019 14:47:41 +0200 Subject: mod_storage_sql: Move code out of if-else chain --- plugins/mod_storage_sql.lua | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua index 56cef569..dbf5e2da 100644 --- a/plugins/mod_storage_sql.lua +++ b/plugins/mod_storage_sql.lua @@ -390,6 +390,14 @@ function archive_store:delete(username, query) else args[#args+1] = query.truncate; local unlimited = "ALL"; + sql_query = [[ + DELETE FROM "prosodyarchive" + WHERE "sort_id" IN ( + SELECT "sort_id" FROM "prosodyarchive" + WHERE %s + ORDER BY "sort_id" %s + LIMIT %s OFFSET ? + );]]; if engine.params.driver == "SQLite3" then sql_query = [[ DELETE FROM "prosodyarchive" @@ -407,15 +415,6 @@ function archive_store:delete(username, query) LIMIT %s OFFSET ? ) AS limiter on result.sort_id = limiter.sort_id;]]; unlimited = "18446744073709551615"; - else - sql_query = [[ - DELETE FROM "prosodyarchive" - WHERE "sort_id" IN ( - SELECT "sort_id" FROM "prosodyarchive" - WHERE %s - ORDER BY "sort_id" %s - LIMIT %s OFFSET ? - );]]; end sql_query = string.format(sql_query, t_concat(where, " AND "), query.reverse and "ASC" or "DESC", unlimited); -- cgit v1.2.3 From 9838f3732d53e1e3d34cd57a25a030f61005cb53 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 13 May 2019 14:39:38 +0200 Subject: mod_storage_sql: Handle SQLite DELETE with LIMIT being optional (fixes #1359) --- plugins/mod_storage_sql.lua | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua index dbf5e2da..a449091e 100644 --- a/plugins/mod_storage_sql.lua +++ b/plugins/mod_storage_sql.lua @@ -399,12 +399,14 @@ function archive_store:delete(username, query) LIMIT %s OFFSET ? );]]; if engine.params.driver == "SQLite3" then - sql_query = [[ - DELETE FROM "prosodyarchive" - WHERE %s - ORDER BY "sort_id" %s - LIMIT %s OFFSET ?; - ]]; + if engine._have_delete_limit then + sql_query = [[ + DELETE FROM "prosodyarchive" + WHERE %s + ORDER BY "sort_id" %s + LIMIT %s OFFSET ?; + ]]; + end unlimited = "-1"; elseif engine.params.driver == "MySQL" then sql_query = [[ @@ -620,6 +622,13 @@ function module.load() module:log("error", "Old database format detected. Please run: prosodyctl mod_%s upgrade", module.name); return false, "database upgrade needed"; end + if engine.params.driver == "SQLite3" then + for row in engine:select("PRAGMA compile_options") do + if row[1] == "ENABLE_UPDATE_DELETE_LIMIT" then + engine._have_delete_limit = true; + end + end + end end end); engines[sql.db2uri(params)] = engine; -- cgit v1.2.3 From c85ae3be7853cde7e08c2afad290770994f4c851 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 18 May 2019 17:45:20 +0200 Subject: mod_c2s: Associate connection with session last (fixes #1313) This way, any fatal error in the callback will not leave a half-established session. --- plugins/mod_c2s.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua index 8e31a968..15d3a9be 100644 --- a/plugins/mod_c2s.lua +++ b/plugins/mod_c2s.lua @@ -239,7 +239,6 @@ end --- Port listener function listener.onconnect(conn) local session = sm_new_session(conn); - sessions[conn] = session; session.log("info", "Client connected"); @@ -300,6 +299,8 @@ function listener.onconnect(conn) end session.dispatch_stanza = stream_callbacks.handlestanza; + + sessions[conn] = session; end function listener.onincoming(conn, data) -- cgit v1.2.3 From db3b9bf5e54e8754d374ea18a444dec777d0f24c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 18 May 2019 17:28:21 +0200 Subject: util.random: Handle unlikely read errors from /dev/urandom (see #1313) --- util/random.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/util/random.lua b/util/random.lua index d8a84514..8ae06b49 100644 --- a/util/random.lua +++ b/util/random.lua @@ -12,7 +12,11 @@ if ok then return crand; end local urandom, urandom_err = io.open("/dev/urandom", "r"); local function bytes(n) - return urandom:read(n); + local data, err = urandom:read(n); + if not data then + error("Unable to retrieve data from secure random number generator (/dev/urandom): "..err); + end + return data; end if not urandom then -- cgit v1.2.3 From f430272c0164496c91a86ad5e027248afda401d4 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 18 May 2019 18:51:25 +0200 Subject: util.random: Coerce error to string (thanks waqas) In theory this could happen in an EOF condition, which should be impossible with a read from /dev/urandom. --- util/random.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/random.lua b/util/random.lua index 8ae06b49..51f2747d 100644 --- a/util/random.lua +++ b/util/random.lua @@ -14,7 +14,7 @@ local urandom, urandom_err = io.open("/dev/urandom", "r"); local function bytes(n) local data, err = urandom:read(n); if not data then - error("Unable to retrieve data from secure random number generator (/dev/urandom): "..err); + error("Unable to retrieve data from secure random number generator (/dev/urandom): "..tostring(err)); end return data; end -- cgit v1.2.3 From 069408d15861035e9da8052068c60939be938ab3 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 21 May 2019 08:52:21 +0200 Subject: util.random: Throw different error for EOL condition --- util/random.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/util/random.lua b/util/random.lua index 51f2747d..6782d7fa 100644 --- a/util/random.lua +++ b/util/random.lua @@ -14,7 +14,11 @@ local urandom, urandom_err = io.open("/dev/urandom", "r"); local function bytes(n) local data, err = urandom:read(n); if not data then - error("Unable to retrieve data from secure random number generator (/dev/urandom): "..tostring(err)); + if err then + error("Unable to retrieve data from secure random number generator (/dev/urandom): "..tostring(err)); + else + error("Secure random number generator (/dev/urandom) returned an end-of-file condition"); + end end return data; end -- cgit v1.2.3 From 05f81e425ccb058a8a944bfbe3eb604cb5d5e96b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 27 May 2019 12:05:35 +0200 Subject: mod_announce: Check for admin on current virtualhost instead of global (fixes #1365) (thanks yc) --- plugins/mod_announce.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_announce.lua b/plugins/mod_announce.lua index 14bb9f3d..970a273a 100644 --- a/plugins/mod_announce.lua +++ b/plugins/mod_announce.lua @@ -44,7 +44,7 @@ function handle_announcement(event) return; -- Not an announcement end - if not is_admin(stanza.attr.from) then + if not is_admin(stanza.attr.from, host) then -- Not an admin? Not allowed! module:log("warn", "Non-admin '%s' tried to send server announcement", stanza.attr.from); return; -- cgit v1.2.3 From 837334916e773f4e69812057db281e0e2c549129 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 27 May 2019 19:00:32 +0200 Subject: mod_mam: Cache last date that archive owner has messages to reduce writes (fixes #1368) --- plugins/mod_mam/mod_mam.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index a8c2689d..8900a2be 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -348,8 +348,14 @@ if cleanup_after ~= "never" then -- messages, we collect the union of sets of users from dates that fall -- outside the cleanup range. + local last_date = require "util.cache".new(module:get_option_number("archive_cleanup_date_cache_size", 1000)); function schedule_cleanup(username, date) - cleanup_map:set(date or datestamp(), username, true); + date = date or datestamp(); + if last_date:get(username) == date then return end + local ok = cleanup_map:set(date, username, true); + if ok then + last_date:set(username, date); + end end cleanup_runner = require "util.async".runner(function () -- cgit v1.2.3 From e96902f6b477fe31d599ae9abf50e9baf0489d50 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 27 May 2019 19:00:34 +0200 Subject: mod_muc_mam: Cache last date that archive owner has messages to reduce writes (fixes #1368) --- plugins/mod_muc_mam.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 1fbc8b2a..a2e3f81b 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -422,8 +422,14 @@ if cleanup_after ~= "never" then -- messages, we collect the union of sets of rooms from dates that fall -- outside the cleanup range. + local last_date = require "util.cache".new(module:get_option_number("muc_log_cleanup_date_cache_size", 1000)); function schedule_cleanup(roomname, date) - cleanup_map:set(date or datestamp(), roomname, true); + date = date or datestamp(); + if last_date:get(roomname) == date then return end + local ok = cleanup_map:set(date, roomname, true); + if ok then + last_date:set(roomname, date); + end end cleanup_runner = require "util.async".runner(function () -- cgit v1.2.3 From 3b49129ad4f9266b525df1ff73baf7c6a58a1b58 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 30 May 2019 15:16:56 +0200 Subject: mod_pep: Handle presence based subscription outside of util.pubsub (fixes #1372) Subscriptions were updated for each incoming presence stanza from contacts. Each subscription change triggered a configuration save, which would filter out the presence based subscriptions and usually end up replacing the existing data with identical data. With many subscribed nodes this adds up to a fair bit of IO bound work that is avoided by keeping them separate. --- plugins/mod_pep.lua | 54 +++++++++++++++++------------------------------------ 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/plugins/mod_pep.lua b/plugins/mod_pep.lua index 7a4aac2b..f60f84c9 100644 --- a/plugins/mod_pep.lua +++ b/plugins/mod_pep.lua @@ -90,21 +90,6 @@ local function nodestore(username) return data, err; end function store:set(node, data) - if data then - -- Save the data without subscriptions - local subscribers = {}; - for jid, sub in pairs(data.subscribers) do - if type(sub) ~= "table" or not sub.presence then - subscribers[jid] = sub; - end - end - data = { - name = data.name; - config = data.config; - affiliations = data.affiliations; - subscribers = subscribers; - }; - end return node_config:set(username, node, data); end function store:users() @@ -151,7 +136,23 @@ local function get_broadcaster(username) end message:add_child(item); end + + local broadcast_to = {}; for jid in pairs(jids) do + broadcast_to[jid] = true; + end + + local service_recipients = recipients[username]; + if service_recipients then + local service = services[username]; + for recipient, nodes in pairs(service_recipients) do + if nodes:contains(node) and service:may(node, recipient, "subscribe") then + broadcast_to[recipient] = true; + end + end + end + + for jid in pairs(broadcast_to) do module:log("debug", "Sending notification to %s from %s: %s", jid, user_bare, tostring(item)); message.attr.to = jid; module:send(message); @@ -160,20 +161,6 @@ local function get_broadcaster(username) return simple_broadcast; end -local function on_node_creation(event) - local service = event.service; - local node = event.node; - local username = service.config.pep_username; - - local service_recipients = recipients[username]; - if not service_recipients then return; end - - for recipient, nodes in pairs(service_recipients) do - if nodes:contains(node) then - service:add_subscription(node, recipient, recipient, { presence = true }); - end - end -end function get_pep_service(username) module:log("debug", "get_pep_service(%q)", username); @@ -233,10 +220,6 @@ function get_pep_service(username) return service; end -module:hook("item-added/pep-service", function (event) - local service = event.item.service; - module:hook_object_event(service.events, "node-created", on_node_creation); -end); function handle_pubsub_iq(event) local origin, stanza = event.origin, event.stanza; @@ -303,12 +286,9 @@ local function update_subscriptions(recipient, service_name, nodes) end local service = get_pep_service(service_name); - for node in current - nodes do - service:remove_subscription(node, recipient, recipient); - end for node in nodes - current do - if service:add_subscription(node, recipient, recipient, { presence = true }) then + if service:may(node, recipient, "subscribe") then resend_last_item(recipient, node, service); end end -- cgit v1.2.3 From e6815c87fb538b894839472820d97d644b20b241 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 30 May 2019 23:50:28 +0200 Subject: util.sql: Ignore if tables and indices already exist on creation (fixes #1064) Tested with SQLite3 3.16.2 and 3.27.2 and Postgres 11. MySQL does not support IF NOT EXISTS for indices so not handled here. --- util/sql.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/util/sql.lua b/util/sql.lua index 47900102..00c7b57f 100644 --- a/util/sql.lua +++ b/util/sql.lua @@ -238,6 +238,9 @@ function engine:transaction(...) end function engine:_create_index(index) local sql = "CREATE INDEX \""..index.name.."\" ON \""..index.table.."\" ("; + if self.params.driver ~= "MySQL" then + sql = sql:gsub("^CREATE INDEX", "%1 IF NOT EXISTS"); + end for i=1,#index do sql = sql.."\""..index[i].."\""; if i ~= #index then sql = sql..", "; end @@ -256,6 +259,9 @@ function engine:_create_index(index) end function engine:_create_table(table) local sql = "CREATE TABLE \""..table.name.."\" ("; + do + sql = sql:gsub("^CREATE TABLE", "%1 IF NOT EXISTS"); + end for i,col in ipairs(table.c) do local col_type = col.type; if col_type == "MEDIUMTEXT" and self.params.driver ~= "MySQL" then -- cgit v1.2.3 From a4764762b3dad679f40dadd6eb2eb31ea1d5cace Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 10 Jun 2019 12:58:00 +0200 Subject: mod_pep: Revert 045209b41b3a, caused a regression Adding in all presence based subscriptions in the broadcaster caused resend_last_item() to unintentionally send out more notifications than it should have. --- plugins/mod_pep.lua | 54 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/plugins/mod_pep.lua b/plugins/mod_pep.lua index f60f84c9..7a4aac2b 100644 --- a/plugins/mod_pep.lua +++ b/plugins/mod_pep.lua @@ -90,6 +90,21 @@ local function nodestore(username) return data, err; end function store:set(node, data) + if data then + -- Save the data without subscriptions + local subscribers = {}; + for jid, sub in pairs(data.subscribers) do + if type(sub) ~= "table" or not sub.presence then + subscribers[jid] = sub; + end + end + data = { + name = data.name; + config = data.config; + affiliations = data.affiliations; + subscribers = subscribers; + }; + end return node_config:set(username, node, data); end function store:users() @@ -136,23 +151,7 @@ local function get_broadcaster(username) end message:add_child(item); end - - local broadcast_to = {}; for jid in pairs(jids) do - broadcast_to[jid] = true; - end - - local service_recipients = recipients[username]; - if service_recipients then - local service = services[username]; - for recipient, nodes in pairs(service_recipients) do - if nodes:contains(node) and service:may(node, recipient, "subscribe") then - broadcast_to[recipient] = true; - end - end - end - - for jid in pairs(broadcast_to) do module:log("debug", "Sending notification to %s from %s: %s", jid, user_bare, tostring(item)); message.attr.to = jid; module:send(message); @@ -161,6 +160,20 @@ local function get_broadcaster(username) return simple_broadcast; end +local function on_node_creation(event) + local service = event.service; + local node = event.node; + local username = service.config.pep_username; + + local service_recipients = recipients[username]; + if not service_recipients then return; end + + for recipient, nodes in pairs(service_recipients) do + if nodes:contains(node) then + service:add_subscription(node, recipient, recipient, { presence = true }); + end + end +end function get_pep_service(username) module:log("debug", "get_pep_service(%q)", username); @@ -220,6 +233,10 @@ function get_pep_service(username) return service; end +module:hook("item-added/pep-service", function (event) + local service = event.item.service; + module:hook_object_event(service.events, "node-created", on_node_creation); +end); function handle_pubsub_iq(event) local origin, stanza = event.origin, event.stanza; @@ -286,9 +303,12 @@ local function update_subscriptions(recipient, service_name, nodes) end local service = get_pep_service(service_name); + for node in current - nodes do + service:remove_subscription(node, recipient, recipient); + end for node in nodes - current do - if service:may(node, recipient, "subscribe") then + if service:add_subscription(node, recipient, recipient, { presence = true }) then resend_last_item(recipient, node, service); end end -- cgit v1.2.3 From 074e8143a1e968502888b022bf268ee7c06e0a83 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 20 Jun 2019 21:08:12 +0200 Subject: mod_blocklist: Add test case capturing current behavior --- spec/scansion/blocking.scs | 162 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 spec/scansion/blocking.scs diff --git a/spec/scansion/blocking.scs b/spec/scansion/blocking.scs new file mode 100644 index 00000000..5f4c8cba --- /dev/null +++ b/spec/scansion/blocking.scs @@ -0,0 +1,162 @@ +# XEP-0191: Blocking Command + +[Client] Romeo + jid: blocker@localhost + password: password + +[Client] Juliet + jid: blockee@localhost + password: password + +----- + +# The parties connect +Romeo connects + +Romeo sends: + + +Romeo receives: + + + + +Juliet connects + +Juliet sends: + + +Juliet receives: + + + + +# They add each other +Romeo sends: + + +Romeo receives: + + +Juliet receives: + + +Juliet sends: + + +Romeo receives: + + + + + +Juliet sends: + + +Juliet receives: + + +Romeo receives: + + +Romeo sends: + + +Juliet receives: + + + + + +Romeo receives: + + + + + +# They can now talk +Juliet sends: + + ohai + + +Romeo receives: + + ohai + + +# And now to the blockining + +Romeo sends: + + + + + + +Romeo receives: + + +Juliet receives: + + +# Can"t talk anymore +Romeo sends: + + hello? + + +Romeo receives: + + + + You have blocked this JID + + + + +Juliet sends: + + +Juliet receives: + + + + + + +Romeo sends: + + + + + + +Romeo receives: + + +# Can talk again +Romeo sends: + + hello! + + +Juliet receives: + + hello! + + +# Bye +Juliet disconnects + +Juliet sends: + + +Romeo receives: + + +Romeo disconnects + +Romeo sends: + + -- cgit v1.2.3 From 66660dfd85604346d39a5d237ca5a496b43a292b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 20 Jun 2019 21:18:46 +0200 Subject: mod_blocklist: Update test case with correct behavior (see #1380) Expect failure --- spec/scansion/blocking.scs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/scansion/blocking.scs b/spec/scansion/blocking.scs index 5f4c8cba..809bbcb5 100644 --- a/spec/scansion/blocking.scs +++ b/spec/scansion/blocking.scs @@ -132,6 +132,12 @@ Romeo sends: +Juliet receives: + + + + + Romeo receives: -- cgit v1.2.3 From c6ad506b85a5740f55c9b12896f92ee15e74abce Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 20 Jun 2019 21:20:00 +0200 Subject: mod_blocklist: Trigger resend of presence when unblocking a contact (fixes #1380) --- plugins/mod_blocklist.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/mod_blocklist.lua b/plugins/mod_blocklist.lua index 8aca7332..ee48ffad 100644 --- a/plugins/mod_blocklist.lua +++ b/plugins/mod_blocklist.lua @@ -128,6 +128,7 @@ local function edit_blocklist(event) -- > only if the contact is allowed to receive presence notifications [...] -- So contacts we need to do that for are added to the set below. local send_unavailable = is_blocking and {}; + local send_available = not is_blocking and {}; -- Because blocking someone currently also blocks the ability to reject -- subscription requests, we'll preemptively reject such @@ -147,6 +148,8 @@ local function edit_blocklist(event) elseif is_contact_pending_in(username, module.host, jid) then remove_pending[jid] = true; end + elseif is_contact_subscribed(username, module.host, jid) then + send_available[jid] = true; end end @@ -203,6 +206,11 @@ local function edit_blocklist(event) save_roster(username, module.host, roster); -- Not much we can do about save failing here end + else + local user_bare = username .. "@" .. module.host; + for jid in pairs(send_available) do + module:send(st.presence({ type = "probe", to = user_bare, from = jid })); + end end local blocklist_push = st.iq({ type = "set", id = "blocklist-push" }) -- cgit v1.2.3 From 159ef78300120f98bd1620418eac69e85fdcb445 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 20 Jun 2019 22:10:19 +0200 Subject: mod_blocklist: Remove unrelated tags from test case --- spec/scansion/blocking.scs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/spec/scansion/blocking.scs b/spec/scansion/blocking.scs index 809bbcb5..7b46167f 100644 --- a/spec/scansion/blocking.scs +++ b/spec/scansion/blocking.scs @@ -17,9 +17,7 @@ Romeo sends: Romeo receives: - - - + Juliet connects @@ -27,9 +25,7 @@ Juliet sends: Juliet receives: - - - + # They add each other Romeo sends: @@ -47,7 +43,6 @@ Juliet sends: Romeo receives: - Juliet sends: @@ -65,13 +60,11 @@ Romeo sends: Juliet receives: - Romeo receives: - # They can now talk @@ -134,7 +127,6 @@ Romeo sends: Juliet receives: - -- cgit v1.2.3 From 2033d552cb3819def72ef2507d3dcb0fabfa343e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 20 Jun 2019 22:25:46 +0200 Subject: scansion/blocking: Remove the right irrelevant thing --- spec/scansion/blocking.scs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/scansion/blocking.scs b/spec/scansion/blocking.scs index 7b46167f..6a9f199e 100644 --- a/spec/scansion/blocking.scs +++ b/spec/scansion/blocking.scs @@ -127,7 +127,7 @@ Romeo sends: Juliet receives: - + Romeo receives: -- cgit v1.2.3 From dd837c451e8a09051fb042d7eded02a124dfb75e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 8 Jul 2019 01:17:34 +0200 Subject: net.server_epoll: Backport timer optimization 6c2370f17027 from trunk (see #1388) The previous timer handling did not scale well and led to high CPU usage with many connections (each with at least an read timeout). --- net/server_epoll.lua | 77 ++++++++++++++++++++-------------------------------- 1 file changed, 29 insertions(+), 48 deletions(-) diff --git a/net/server_epoll.lua b/net/server_epoll.lua index c279c579..0c03ae15 100644 --- a/net/server_epoll.lua +++ b/net/server_epoll.lua @@ -6,9 +6,7 @@ -- -local t_sort = table.sort; local t_insert = table.insert; -local t_remove = table.remove; local t_concat = table.concat; local setmetatable = setmetatable; local tostring = tostring; @@ -20,6 +18,7 @@ local log = require "util.logger".init("server_epoll"); local socket = require "socket"; local luasec = require "ssl"; local gettime = require "util.time".now; +local indexedbheap = require "util.indexedbheap"; local createtable = require "util.table".create; local inet = require "util.net"; local inet_pton = inet.pton; @@ -66,22 +65,24 @@ local fds = createtable(10, 0); -- FD -> conn -- Timer and scheduling -- -local timers = {}; +local timers = indexedbheap.create(); local function noop() end local function closetimer(t) t[1] = 0; t[2] = noop; + timers:remove(t.id); end --- Set to true when timers have changed -local resort_timers = false; +local function reschedule(t, time) + t[1] = time; + timers:reprioritize(t.id, time); +end -- Add absolute timer local function at(time, f) - local timer = { time, f, close = closetimer }; - t_insert(timers, timer); - resort_timers = true; + local timer = { time, f, close = closetimer, reschedule = reschedule, id = nil }; + timer.id = timers:insert(timer, time); return timer; end @@ -94,50 +95,32 @@ end -- Return time until next timeout local function runtimers(next_delay, min_wait) -- Any timers at all? - if not timers[1] then - return next_delay; + local now = gettime(); + local peek = timers:peek(); + while peek do + + if peek > now then + next_delay = peek - now; + break; end - if resort_timers then - -- Sort earliest timers to the end - t_sort(timers, function (a, b) return a[1] > b[1]; end); - resort_timers = false; + local _, timer, id = timers:pop(); + local ok, ret = pcall(timer[2], now); + if ok and type(ret) == "number" then + local next_time = now+ret; + timer[1] = next_time; + timers:insert(timer, next_time); end - -- Iterate from the end and remove completed timers - for i = #timers, 1, -1 do - local timer = timers[i]; - local t, f = timer[1], timer[2]; - -- Get time for every iteration to increase accuracy - local now = gettime(); - if t > now then - -- This timer should not fire yet - local diff = t - now; - if diff < next_delay then - next_delay = diff; + peek = timers:peek(); end - break; - end - local new_timeout = f(now); - if new_timeout then - -- Schedule for 'delay' from the time actually scheduled, - -- not from now, in order to prevent timer drift. - timer[1] = t + new_timeout; - resort_timers = true; - else - t_remove(timers, i); - end + if peek == nil then + return next_delay; end - if resort_timers or next_delay < min_wait then - -- Timers may be added from within a timer callback. - -- Those would not be considered for next_delay, - -- and we might sleep for too long, so instead - -- we return a shorter timeout so we can - -- properly sort all new timers. - next_delay = min_wait; + if next_delay < min_wait then + return min_wait; end - return next_delay; end @@ -243,8 +226,7 @@ function interface:setreadtimeout(t) end t = t or cfg.read_timeout; if self._readtimeout then - self._readtimeout[1] = gettime() + t; - resort_timers = true; + self._readtimeout:reschedule(gettime() + t); else self._readtimeout = addtimer(t, function () if self:on("readtimeout") then @@ -268,8 +250,7 @@ function interface:setwritetimeout(t) end t = t or cfg.send_timeout; if self._writetimeout then - self._writetimeout[1] = gettime() + t; - resort_timers = true; + self._writetimeout:reschedule(gettime() + t); else self._writetimeout = addtimer(t, function () self:on("disconnect", "write timeout"); -- cgit v1.2.3 From 5399b9b9057f8202ceb25e933d10e1825383bc99 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 8 Jul 2019 02:46:27 +0200 Subject: util.serialization: Cache default serialization instance (fixes #1389) Most serialization uses still use the default serialize() and thus duplicate much of the setup, which negates some of the performance improvements of the rewrite. --- util/serialization.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/util/serialization.lua b/util/serialization.lua index dd6a2a2b..5121a9f9 100644 --- a/util/serialization.lua +++ b/util/serialization.lua @@ -272,10 +272,15 @@ local function deserialize(str) return ret; end +local default = new(); return { new = new; serialize = function (x, opt) - return new(opt)(x); + if opt == nil then + return default(x); + else + return new(opt)(x); + end end; deserialize = deserialize; }; -- cgit v1.2.3 From 022198143dcc0d586030d183ac8ca3368ff6172b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 20 Jul 2019 04:19:58 +0200 Subject: mod_websocket: Clone stanza before mutating (fixes #1398) Checking for `stanza.attr.xmlns == nil` to determine if the stanza object is an actual stanza (``, `` or `` in the `jabber:client` or `jabbber:server` namespace) or some other stream element. Since this mutation is not reverted, it may leak to other places and cause them to mistreat stanzas as stream elements. Especially in cases like MUC where a single stanza is broadcast to many recipients. --- plugins/mod_websocket.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index b4aba338..686a8981 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -285,6 +285,7 @@ function handle_request(event) end); add_filter(session, "stanzas/out", function(stanza) + stanza = st.clone(stanza); local attr = stanza.attr; attr.xmlns = attr.xmlns or xmlns_client; if stanza.name:find("^stream:") then -- cgit v1.2.3 From 12431d51d7746c4c1a70fccb2a6ec081b13d1b93 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 30 Jul 2019 08:01:22 +0200 Subject: MUC: Advertise XEP-0410 support Unsure if the feature was in the XEP at the time of 7c1cdf5f9f83 --- plugins/muc/muc.lib.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 9648ea78..a942182d 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -358,6 +358,7 @@ end module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var = "http://jabber.org/protocol/muc"}):up(); event.reply:tag("feature", {var = "http://jabber.org/protocol/muc#stable_id"}):up(); + event.reply:tag("feature", {var = "http://jabber.org/protocol/muc#self-ping-optimization"}):up(); end); module:hook("muc-disco#info", function(event) table.insert(event.form, { name = "muc#roominfo_occupants", label = "Number of occupants" }); -- cgit v1.2.3 From 8a309e53bd73871538ad27a873dd6740168ad4ce Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 31 Aug 2019 16:08:45 +0200 Subject: MUC: Fix delay@from to be room JID (fixes #1416) --- plugins/muc/history.lib.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/muc/history.lib.lua b/plugins/muc/history.lib.lua index 445aacb9..0d69c97d 100644 --- a/plugins/muc/history.lib.lua +++ b/plugins/muc/history.lib.lua @@ -178,10 +178,10 @@ module:hook("muc-add-history", function(event) local ts = gettime(); local stamp = datetime.datetime(ts); stanza:tag("delay", { -- XEP-0203 - xmlns = "urn:xmpp:delay", from = module.host, stamp = stamp + xmlns = "urn:xmpp:delay", from = room.jid, stamp = stamp }):up(); stanza:tag("x", { -- XEP-0091 (deprecated) - xmlns = "jabber:x:delay", from = module.host, stamp = datetime.legacy() + xmlns = "jabber:x:delay", from = room.jid, stamp = datetime.legacy() }):up(); local entry = { stanza = stanza, timestamp = ts }; table.insert(history, entry); -- cgit v1.2.3 -- cgit v1.2.3 From f78abda947708f72af2eb948b7ed52f08fd39fe4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 11 Sep 2019 15:03:54 +0100 Subject: prosodyctl: Fix traceback on incorrect proxy65_address type --- prosodyctl | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/prosodyctl b/prosodyctl index e580446b..405ef1f8 100755 --- a/prosodyctl +++ b/prosodyctl @@ -1103,17 +1103,21 @@ function commands.check(arg) if modules:contains("proxy65") then local proxy65_target = configmanager.get(host, "proxy65_address") or host; - local A, AAAA = dns.lookup(idna.to_ascii(proxy65_target), "A"), dns.lookup(idna.to_ascii(proxy65_target), "AAAA"); - local prob = {}; - if not A then - table.insert(prob, "A"); - end - if v6_supported and not AAAA then - table.insert(prob, "AAAA"); - end - if #prob > 0 then - print(" File transfer proxy "..proxy65_target.." has no "..table.concat(prob, "/") - .." record. Create one or set 'proxy65_address' to the correct host/IP."); + if type(proxy65_target) == "string" then + local A, AAAA = dns.lookup(idna.to_ascii(proxy65_target), "A"), dns.lookup(idna.to_ascii(proxy65_target), "AAAA"); + local prob = {}; + if not A then + table.insert(prob, "A"); + end + if v6_supported and not AAAA then + table.insert(prob, "AAAA"); + end + if #prob > 0 then + print(" File transfer proxy "..proxy65_target.." has no "..table.concat(prob, "/") + .." record. Create one or set 'proxy65_address' to the correct host/IP."); + end + else + print(" proxy65_address for "..host.." should be set to a string, unable to perform DNS check"); end end -- cgit v1.2.3 From 70427ffd2b92953d4868ed9fd97ea1bf0480c492 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 21 Sep 2019 15:20:50 +0200 Subject: mod_vcard_legacy: Advertise lack of avatar correctly (fixes #1431) (thanks lovetox) --- plugins/mod_vcard_legacy.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_vcard_legacy.lua b/plugins/mod_vcard_legacy.lua index ab2c4490..6229b931 100644 --- a/plugins/mod_vcard_legacy.lua +++ b/plugins/mod_vcard_legacy.lua @@ -301,10 +301,10 @@ local function inject_xep153(event) local pep_service = mod_pep.get_pep_service(username); stanza:remove_children("x", "vcard-temp:x:update"); - local x_update = st.stanza("x", { xmlns = "vcard-temp:x:update" }); + local x_update = st.stanza("x", { xmlns = "vcard-temp:x:update" }):tag("photo"); local ok, avatar_hash = pep_service:get_last_item("urn:xmpp:avatar:metadata", true); if ok and avatar_hash then - x_update:text_tag("photo", avatar_hash); + x_update:text(avatar_hash); end stanza:add_direct_child(x_update); end -- cgit v1.2.3 From ae7a763b275c6306e6c6dcb19e624c1255158d8e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 21 Sep 2019 16:29:16 +0200 Subject: mod_vcard_legacy: Don't owerwrite empty photo elements (fixes #1432) --- plugins/mod_vcard_legacy.lua | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugins/mod_vcard_legacy.lua b/plugins/mod_vcard_legacy.lua index 6229b931..5e75947a 100644 --- a/plugins/mod_vcard_legacy.lua +++ b/plugins/mod_vcard_legacy.lua @@ -300,13 +300,19 @@ local function inject_xep153(event) if stanza.attr.type then return end local pep_service = mod_pep.get_pep_service(username); - stanza:remove_children("x", "vcard-temp:x:update"); - local x_update = st.stanza("x", { xmlns = "vcard-temp:x:update" }):tag("photo"); + local x_update = stanza:get_child("x", "vcard-temp:x:update"); + if not x_update then + x_update = st.stanza("x", { xmlns = "vcard-temp:x:update" }):tag("photo"); + stanza:add_direct_child(x_update); + elseif x_update:get_child("photo") then + return; -- XEP implies that these should be left alone + else + x_update:tag("photo"); + end local ok, avatar_hash = pep_service:get_last_item("urn:xmpp:avatar:metadata", true); if ok and avatar_hash then x_update:text(avatar_hash); end - stanza:add_direct_child(x_update); end module:hook("pre-presence/full", inject_xep153, 1); -- cgit v1.2.3 From dfa96d659ccfe41766f37767faef45fc5a8ae60e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 29 Sep 2019 20:48:00 +0200 Subject: MUC: Strip tags with MUC-related namespaces from private messages (fixes #1427) Prevents duplication since it adds another <{muc#user}x> here --- plugins/muc/muc.lib.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index a942182d..cf9b46f7 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -779,6 +779,7 @@ function room_mt:handle_message_to_occupant(origin, stanza) return true; end log("debug", "%s sent private message stanza to %s (%s)", from, to, o_data.jid); + stanza = muc_util.filter_muc_x(st.clone(stanza)); stanza:tag("x", { xmlns = "http://jabber.org/protocol/muc#user" }):up(); stanza.attr.from = current_nick; self:route_to_occupant(o_data, stanza) -- cgit v1.2.3 From 811c707639eba97d17d90e45606454fad75e5c32 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 3 Oct 2019 16:38:15 +0100 Subject: mod_mam: Add flag to session when it performs a MAM query --- plugins/mod_mam/mod_mam.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index 8900a2be..b500770f 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -90,6 +90,8 @@ module:hook("iq-set/self/"..xmlns_mam..":query", function(event) local query = stanza.tags[1]; local qid = query.attr.queryid; + origin.mam_requested = true; + get_prefs(origin.username, true); -- Search query parameters -- cgit v1.2.3 From 86b23ae5f736829e3c86fa47c1c1047c49b449e6 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 20 Oct 2019 01:20:11 +0200 Subject: MUC: Don't advertise registration feature on host JID (fixes #1451) There is currently no mention in XEP-0045 of how or where to advertise support for registration. Advertising on the host JID may be confusable with service-wide registration, as implemented in ejabberd. A common and sensible pattern in XMPP is that a feature is advertised on the JID where the service is available. --- plugins/muc/register.lib.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/muc/register.lib.lua b/plugins/muc/register.lib.lua index 21cb3f2f..95ed1a84 100644 --- a/plugins/muc/register.lib.lua +++ b/plugins/muc/register.lib.lua @@ -48,8 +48,6 @@ module:hook("muc-set-affiliation", function (event) event.room._reserved_nicks = nil; end); -module:add_feature("jabber:iq:register"); - module:hook("muc-disco#info", function (event) event.reply:tag("feature", { var = "jabber:iq:register" }):up(); end); -- cgit v1.2.3 From e55d816cdd519667a206149099a60e2ee6b5a91d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 20 Oct 2019 20:52:14 +0200 Subject: util.interpolation: Support unescaped variables with more modifiers (fixes #1452) Tests will be added in trunk. --- util/interpolation.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/interpolation.lua b/util/interpolation.lua index 315cc203..e0ccf47b 100644 --- a/util/interpolation.lua +++ b/util/interpolation.lua @@ -32,7 +32,7 @@ local function new_render(pat, escape, funcs) -- assert(type(values) == "table", "bad argument #2 to 'render' (table expected)"); return (s_gsub(template, pat, function (block) block = s_sub(block, 2, -2); - local name, opt, e = s_match(block, "^([%a_][%w_.]*)(%p?)()"); + local name, raw, opt, e = s_match(block, "^([%a_][%w_.]*)(!?)(%p?)()"); if not name then return end local value = values[name]; if not value and name:find(".", 2, true) then @@ -45,7 +45,7 @@ local function new_render(pat, escape, funcs) if funcs then while value ~= nil and opt == '|' do local f; - f, opt, e = s_match(block, "^([%a_][%w_.]*)(%p?)()", e); + f, raw, opt, e = s_match(block, "^([%a_][%w_.]*)(!?)(%p?)()", e); f = funcs[f]; if f then value = f(value); end end @@ -70,7 +70,7 @@ local function new_render(pat, escape, funcs) if type(value) ~= "string" then value = tostring(value); end - if opt ~= '!' then + if raw ~= '!' then return escape(value); end return value; -- cgit v1.2.3 From 24c8edca2a25a6731d9924abc91b802e6d17e56c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 2 Nov 2019 19:24:26 +0100 Subject: net.resolvers: Apply IDNA conversion to ascii for DNS lookups (fixes #1426) --- net/resolvers/basic.lua | 3 ++- net/resolvers/service.lua | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/net/resolvers/basic.lua b/net/resolvers/basic.lua index 9a3c9952..d8031a6b 100644 --- a/net/resolvers/basic.lua +++ b/net/resolvers/basic.lua @@ -1,5 +1,6 @@ local adns = require "net.adns"; local inet_pton = require "util.net".pton; +local idna_to_ascii = require "util.encodings".idna.to_ascii; local methods = {}; local resolver_mt = { __index = methods }; @@ -59,7 +60,7 @@ end local function new(hostname, port, conn_type, extra) return setmetatable({ - hostname = hostname; + hostname = idna_to_ascii(hostname); port = port; conn_type = conn_type or "tcp"; extra = extra; diff --git a/net/resolvers/service.lua b/net/resolvers/service.lua index b5a2d821..a76adb73 100644 --- a/net/resolvers/service.lua +++ b/net/resolvers/service.lua @@ -1,5 +1,6 @@ local adns = require "net.adns"; local basic = require "net.resolvers.basic"; +local idna_to_ascii = require "util.encodings".idna.to_ascii; local methods = {}; local resolver_mt = { __index = methods }; @@ -58,7 +59,7 @@ end local function new(hostname, service, conn_type, extra) return setmetatable({ - hostname = hostname; + hostname = idna_to_ascii(hostname); service = service; conn_type = conn_type or "tcp"; extra = extra; -- cgit v1.2.3 From ae75d537f6a9f175f2ded30e0866aa2c092872f0 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 2 Nov 2019 19:36:19 +0100 Subject: net.resolvers: Abort on hostnames not passing IDNA validation Prevents error on trying to use nil. Needs better error reporting in the future. --- net/resolvers/basic.lua | 5 +++++ net/resolvers/service.lua | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/net/resolvers/basic.lua b/net/resolvers/basic.lua index d8031a6b..cafef58b 100644 --- a/net/resolvers/basic.lua +++ b/net/resolvers/basic.lua @@ -18,6 +18,11 @@ function methods:next(cb) return; end + if not self.hostname then + -- FIXME report IDNA error + cb(nil); + end + local targets = {}; local n = 2; local function ready() diff --git a/net/resolvers/service.lua b/net/resolvers/service.lua index a76adb73..bb255c33 100644 --- a/net/resolvers/service.lua +++ b/net/resolvers/service.lua @@ -25,6 +25,11 @@ function methods:next(cb) return; end + if not self.hostname then + -- FIXME report IDNA error + cb(nil); + end + local targets = {}; local function ready() self.targets = targets; -- cgit v1.2.3 From 64b86ad50602aae90cee6bf0de5647f5e90287b2 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 3 Nov 2019 22:19:09 +0100 Subject: util.startup: Update config path (fixes #1430) --- util/startup.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/startup.lua b/util/startup.lua index c101c290..82d0804d 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -33,7 +33,8 @@ function startup.read_config() if file then file:close(); prosody.config_file = filename; - CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$"); -- luacheck: ignore 111 + prosody.paths.config = filename:match("^(.*)[\\/][^\\/]*$"); + CFG_CONFIGDIR = prosody.paths.config; -- luacheck: ignore 111 break; end end -- cgit v1.2.3 From 65bbfdb65b7abecb941537de863e4856e5b637cd Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 4 Nov 2019 00:29:49 +0100 Subject: util.startup: Ensure prosody.paths are absolute (see #1430) Normally these paths are injected into the installed 'prosody' executable as absolute paths, but it is possible to override at least the config path via environment variable or command line argument. This makes sure a path relative to pwd stays relative to that instead of the data directory. --- util/startup.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/util/startup.lua b/util/startup.lua index 82d0804d..e88ed709 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -231,8 +231,14 @@ end function startup.chdir() if prosody.installed then + local lfs = require "lfs"; + -- Ensure paths are absolute, not relative to the working directory which we're about to change + local cwd = lfs.currentdir(); + prosody.paths.source = config.resolve_relative_path(cwd, prosody.paths.source); + prosody.paths.config = config.resolve_relative_path(cwd, prosody.paths.config); + prosody.paths.data = config.resolve_relative_path(cwd, prosody.paths.data); -- Change working directory to data path. - require "lfs".chdir(prosody.paths.data); + lfs.chdir(prosody.paths.data); end end -- cgit v1.2.3 From f57d4a439ae2a3b013703e9091b549b7ba315d64 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 8 Nov 2019 00:54:56 +0100 Subject: net.resolvers: Fix traceback from hostname failing IDNA Related to #1426 --- net/resolvers/basic.lua | 1 + net/resolvers/service.lua | 1 + 2 files changed, 2 insertions(+) diff --git a/net/resolvers/basic.lua b/net/resolvers/basic.lua index cafef58b..f37e74a2 100644 --- a/net/resolvers/basic.lua +++ b/net/resolvers/basic.lua @@ -21,6 +21,7 @@ function methods:next(cb) if not self.hostname then -- FIXME report IDNA error cb(nil); + return; end local targets = {}; diff --git a/net/resolvers/service.lua b/net/resolvers/service.lua index bb255c33..34f14cba 100644 --- a/net/resolvers/service.lua +++ b/net/resolvers/service.lua @@ -28,6 +28,7 @@ function methods:next(cb) if not self.hostname then -- FIXME report IDNA error cb(nil); + return; end local targets = {}; -- cgit v1.2.3 From 6a2b94e8b8629d6571202a34b6ebbd4793fe4abe Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 16 Nov 2019 16:45:33 +0100 Subject: util.pposix: Avoid overflow of malloc info at 2GB (fixes #1445 until 4GB) --- util-src/pposix.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/util-src/pposix.c b/util-src/pposix.c index 5c926603..004f61a6 100644 --- a/util-src/pposix.c +++ b/util-src/pposix.c @@ -721,20 +721,20 @@ int lc_meminfo(lua_State *L) { struct mallinfo info = mallinfo(); lua_createtable(L, 0, 5); /* This is the total size of memory allocated with sbrk by malloc, in bytes. */ - lua_pushinteger(L, info.arena); + lua_pushinteger(L, (unsigned)info.arena); lua_setfield(L, -2, "allocated"); /* This is the total size of memory allocated with mmap, in bytes. */ - lua_pushinteger(L, info.hblkhd); + lua_pushinteger(L, (unsigned)info.hblkhd); lua_setfield(L, -2, "allocated_mmap"); /* This is the total size of memory occupied by chunks handed out by malloc. */ - lua_pushinteger(L, info.uordblks); + lua_pushinteger(L, (unsigned)info.uordblks); lua_setfield(L, -2, "used"); /* This is the total size of memory occupied by free (not in use) chunks. */ - lua_pushinteger(L, info.fordblks); + lua_pushinteger(L, (unsigned)info.fordblks); lua_setfield(L, -2, "unused"); /* This is the size of the top-most releasable chunk that normally borders the end of the heap (i.e., the high end of the virtual address space's data segment). */ - lua_pushinteger(L, info.keepcost); + lua_pushinteger(L, (unsigned)info.keepcost); lua_setfield(L, -2, "returnable"); return 1; } -- cgit v1.2.3 From 17e77058d6d3a1e936f8d108a70dde4b3dd296a2 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 23 Nov 2019 01:05:28 +0100 Subject: MUC: Keep role across nickname change (fixes #1466) --- plugins/muc/muc.lib.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index cf9b46f7..639ecc38 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -524,6 +524,9 @@ function room_mt:handle_normal_presence(origin, stanza) log("debug", "no occupant found for %s; creating new occupant object for %s", dest_jid, real_jid); is_first_dest_session = true; dest_occupant = self:new_occupant(bare_jid, dest_jid); + if orig_occupant then + dest_occupant.role = orig_occupant.role; + end else is_first_dest_session = false; end -- cgit v1.2.3 From 704b463d2995c6981c6a96e55ee4196f580f5ea6 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 23 Nov 2019 23:10:39 +0100 Subject: prosodyctl check: Warn about conflict between mod_vcard and mod_vcard_legacy (#1469) --- prosodyctl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/prosodyctl b/prosodyctl index 405ef1f8..a732a73e 100755 --- a/prosodyctl +++ b/prosodyctl @@ -933,6 +933,10 @@ function commands.check(arg) print(" For more information see https://prosody.im/doc/storage"); end end + if all_modules:contains("vcard") and all_modules:contains("vcard_legacy") then + print(" Both mod_vcard_legacy and mod_vcard are enabled but they conflict"); + print(" with each other. Remove one."); + end for host, host_config in pairs(config) do --luacheck: ignore 213/host if type(rawget(host_config, "storage")) == "string" and rawget(host_config, "default_storage") then print(""); -- cgit v1.2.3 From 6a5c40e1cac89398fa56b15459ba3f1777889d85 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 23 Nov 2019 23:11:03 +0100 Subject: core.modulemanager: Disable mod_vcard if mod_vcard_legacy is enabled to prevent conflict (#1469) --- core/modulemanager.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 17602459..a824d36a 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -63,6 +63,11 @@ local function get_modules_for_host(host) modules:add("admin_telnet"); end + if modules:contains("vcard") and modules:contains("vcard_legacy") then + log("error", "The mod_vcard_legacy plugin replaces mod_vcard but both are enabled. Please update your config."); + modules:remove("vcard"); + end + return modules, component; end -- cgit v1.2.3 From 34563dd70dfe21a8bfaedc7f34810b9c6d834be8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 24 Nov 2019 04:23:51 +0100 Subject: net.resolvers.basic: Move IP literal check to constructor This is to prepare for fixing #1459. An IPv6 literal in [ ] brackets does not pass IDNA and resolving it fails there. --- net/resolvers/basic.lua | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/net/resolvers/basic.lua b/net/resolvers/basic.lua index f37e74a2..e3a94382 100644 --- a/net/resolvers/basic.lua +++ b/net/resolvers/basic.lua @@ -33,16 +33,6 @@ function methods:next(cb) self:next(cb); end - local is_ip = inet_pton(self.hostname); - if is_ip then - if #is_ip == 16 then - cb(self.conn_type.."6", self.hostname, self.port, self.extra); - elseif #is_ip == 4 then - cb(self.conn_type.."4", self.hostname, self.port, self.extra); - end - return; - end - -- Resolve DNS to target list local dns_resolver = adns.resolver(); dns_resolver:lookup(function (answer) @@ -65,11 +55,24 @@ function methods:next(cb) end local function new(hostname, port, conn_type, extra) + local ascii_host = idna_to_ascii(hostname); + local targets = nil; + + local is_ip = inet_pton(hostname); + if is_ip then + if #is_ip == 16 then + targets = { { conn_type.."6", hostname, port, extra } }; + elseif #is_ip == 4 then + targets = { { conn_type.."4", hostname, port, extra } }; + end + end + return setmetatable({ - hostname = idna_to_ascii(hostname); + hostname = ascii_host; port = port; conn_type = conn_type or "tcp"; extra = extra; + targets = targets; }, resolver_mt); end -- cgit v1.2.3 From a5450086e824443cff04adc9ed5ec3cca8301151 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 24 Nov 2019 04:26:44 +0100 Subject: net.resolvers.basic: Fix resolution of IPv6 literals (in brackets) (fixes #1459) --- net/resolvers/basic.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/resolvers/basic.lua b/net/resolvers/basic.lua index e3a94382..93182fda 100644 --- a/net/resolvers/basic.lua +++ b/net/resolvers/basic.lua @@ -59,6 +59,9 @@ local function new(hostname, port, conn_type, extra) local targets = nil; local is_ip = inet_pton(hostname); + if not is_ip and hostname:sub(1,1) == '[' then + is_ip = inet_pton(hostname:sub(2,-2)); + end if is_ip then if #is_ip == 16 then targets = { { conn_type.."6", hostname, port, extra } }; -- cgit v1.2.3 From d9c64e1f412ce232e44eac499f006292d8cc720b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 24 Nov 2019 04:41:07 +0100 Subject: net.resolvers.basic: Normalise IP literals, ensures net.server is happy --- net/resolvers/basic.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/resolvers/basic.lua b/net/resolvers/basic.lua index 93182fda..08c71ef5 100644 --- a/net/resolvers/basic.lua +++ b/net/resolvers/basic.lua @@ -1,5 +1,6 @@ local adns = require "net.adns"; local inet_pton = require "util.net".pton; +local inet_ntop = require "util.net".ntop; local idna_to_ascii = require "util.encodings".idna.to_ascii; local methods = {}; @@ -63,6 +64,7 @@ local function new(hostname, port, conn_type, extra) is_ip = inet_pton(hostname:sub(2,-2)); end if is_ip then + hostname = inet_ntop(is_ip); if #is_ip == 16 then targets = { { conn_type.."6", hostname, port, extra } }; elseif #is_ip == 4 then -- cgit v1.2.3 From abff09b5b516ba8d0cf462c1e68e6669f48b8aaa Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 6 Jun 2019 17:37:15 +0200 Subject: util.pubsub: Factor out calling of broadcaster This will simplify doing things along with broadcasting. --- util/pubsub.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/util/pubsub.lua b/util/pubsub.lua index e5e0cb7c..24f1f41d 100644 --- a/util/pubsub.lua +++ b/util/pubsub.lua @@ -499,7 +499,7 @@ function service:delete(node, actor) --> ok, err end self.events.fire_event("node-deleted", { service = self, node = node, actor = actor }); - self.config.broadcaster("delete", node, node_obj.subscribers, nil, actor, node_obj, self); + self:broadcast("delete", node, node_obj.subscribers, nil, actor, node_obj); return true; end @@ -562,10 +562,14 @@ function service:publish(node, actor, id, item, requested_config) --> ok, err local event_data = { service = self, node = node, actor = actor, id = id, item = item }; self.events.fire_event("item-published/"..node, event_data); self.events.fire_event("item-published", event_data); - self.config.broadcaster("items", node, node_obj.subscribers, item, actor, node_obj, self); + self:broadcast("items", node, node_obj.subscribers, item, actor, node_obj); return true; end +function service:broadcast(event, node, subscribers, item, actor, node_obj) + return self.config.broadcaster(event, node, subscribers, item, actor, node_obj, self); +end + function service:retract(node, actor, id, retract) --> ok, err -- Access checking if not self:may(node, actor, "retract") then @@ -582,7 +586,7 @@ function service:retract(node, actor, id, retract) --> ok, err end self.events.fire_event("item-retracted", { service = self, node = node, actor = actor, id = id }); if retract then - self.config.broadcaster("retract", node, node_obj.subscribers, retract, actor, node_obj, self); + self:broadcast("retract", node, node_obj.subscribers, retract, actor, node_obj); end return true end @@ -604,7 +608,7 @@ function service:purge(node, actor, notify) --> ok, err end self.events.fire_event("node-purged", { service = self, node = node, actor = actor }); if notify then - self.config.broadcaster("purge", node, node_obj.subscribers, nil, actor, node_obj, self); + self:broadcast("purge", node, node_obj.subscribers, nil, actor, node_obj); end return true end -- cgit v1.2.3 From 0307e6754aec0011923b2d010103a8c81146cb13 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 6 Jun 2019 18:13:46 +0200 Subject: util.pubsub: Pass subscribers trough a filter callback --- util/pubsub.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/pubsub.lua b/util/pubsub.lua index 24f1f41d..1674b9a7 100644 --- a/util/pubsub.lua +++ b/util/pubsub.lua @@ -6,6 +6,7 @@ local service_mt = {}; local default_config = { itemstore = function (config, _) return cache.new(config["max_items"]) end; broadcaster = function () end; + subscriber_filter = function (subs) return subs end; itemcheck = function () return true; end; get_affiliation = function () end; normalize_jid = function (jid) return jid; end; @@ -567,6 +568,7 @@ function service:publish(node, actor, id, item, requested_config) --> ok, err end function service:broadcast(event, node, subscribers, item, actor, node_obj) + subscribers = self.config.subscriber_filter(subscribers, node, event); return self.config.broadcaster(event, node, subscribers, item, actor, node_obj, self); end -- cgit v1.2.3 From dfdc2502ee41533db43d1aa4d0bc38bc44973ac1 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 10 Jun 2019 13:57:09 +0200 Subject: mod_pep: Handle presence subscriptions in filter (fixes #1372) Take two on 045209b41b3a --- plugins/mod_pep.lua | 51 +++++++++++++++++---------------------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/plugins/mod_pep.lua b/plugins/mod_pep.lua index 7a4aac2b..12be41a2 100644 --- a/plugins/mod_pep.lua +++ b/plugins/mod_pep.lua @@ -90,21 +90,6 @@ local function nodestore(username) return data, err; end function store:set(node, data) - if data then - -- Save the data without subscriptions - local subscribers = {}; - for jid, sub in pairs(data.subscribers) do - if type(sub) ~= "table" or not sub.presence then - subscribers[jid] = sub; - end - end - data = { - name = data.name; - config = data.config; - affiliations = data.affiliations; - subscribers = subscribers; - }; - end return node_config:set(username, node, data); end function store:users() @@ -160,18 +145,23 @@ local function get_broadcaster(username) return simple_broadcast; end -local function on_node_creation(event) - local service = event.service; - local node = event.node; - local username = service.config.pep_username; - - local service_recipients = recipients[username]; - if not service_recipients then return; end +local function get_subscriber_filter(username) + return function (jids, node) + local broadcast_to = {}; + for jid, opts in pairs(jids) do + broadcast_to[jid] = opts; + end - for recipient, nodes in pairs(service_recipients) do - if nodes:contains(node) then - service:add_subscription(node, recipient, recipient, { presence = true }); + local service_recipients = recipients[username]; + if service_recipients then + local service = services[username]; + for recipient, nodes in pairs(service_recipients) do + if nodes:contains(node) and service:may(node, recipient, "subscribe") then + broadcast_to[recipient] = true; + end + end end + return broadcast_to; end end @@ -196,6 +186,7 @@ function get_pep_service(username) nodestore = nodestore(username); itemstore = simple_itemstore(username); broadcaster = get_broadcaster(username); + subscriber_filter = get_subscriber_filter(username); itemcheck = is_item_stanza; get_affiliation = function (jid) if jid_bare(jid) == user_bare then @@ -233,11 +224,6 @@ function get_pep_service(username) return service; end -module:hook("item-added/pep-service", function (event) - local service = event.item.service; - module:hook_object_event(service.events, "node-created", on_node_creation); -end); - function handle_pubsub_iq(event) local origin, stanza = event.origin, event.stanza; local service_name = origin.username; @@ -303,12 +289,9 @@ local function update_subscriptions(recipient, service_name, nodes) end local service = get_pep_service(service_name); - for node in current - nodes do - service:remove_subscription(node, recipient, recipient); - end for node in nodes - current do - if service:add_subscription(node, recipient, recipient, { presence = true }) then + if service:may(node, recipient, "subscribe") then resend_last_item(recipient, node, service); end end -- cgit v1.2.3 From 34b067a5ad009f0efb5bd2c6bb036f4689fe801f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Dec 2019 22:42:39 +0100 Subject: mod_auth_internal_hashed: Pass on errors from password hash function (fixes #1477) --- plugins/mod_auth_internal_hashed.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/mod_auth_internal_hashed.lua b/plugins/mod_auth_internal_hashed.lua index 35764afb..083f648b 100644 --- a/plugins/mod_auth_internal_hashed.lua +++ b/plugins/mod_auth_internal_hashed.lua @@ -68,6 +68,9 @@ function provider.set_password(username, password) account.salt = generate_uuid(); account.iteration_count = max(account.iteration_count or 0, default_iteration_count); local valid, stored_key, server_key = getAuthenticationDatabaseSHA1(password, account.salt, account.iteration_count); + if not valid then + return valid, stored_key; + end local stored_key_hex = to_hex(stored_key); local server_key_hex = to_hex(server_key); @@ -99,6 +102,9 @@ function provider.create_user(username, password) end local salt = generate_uuid(); local valid, stored_key, server_key = getAuthenticationDatabaseSHA1(password, salt, default_iteration_count); + if not valid then + return valid, stored_key; + end local stored_key_hex = to_hex(stored_key); local server_key_hex = to_hex(server_key); return accounts:set(username, { -- cgit v1.2.3 From 83455007ac12820c87bdb637b45ab1b5c45aa1f0 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Dec 2019 23:18:39 +0100 Subject: mod_mam: Log error when unable to store stanza (fix #1478) Unclear what else to do here. Caught by luacheck --- plugins/mod_mam/mod_mam.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index b500770f..2443270f 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -305,6 +305,8 @@ local function message_handler(event, c2s) event.stanza = clone_for_other_handlers; schedule_cleanup(store_user); module:fire_event("archive-message-added", { origin = origin, stanza = clone_for_storage, for_user = store_user, id = id }); + else + log("error", "Could not archive stanza: %s", err); end else log("debug", "Not archiving stanza: %s (prefs)", stanza:top_tag()); -- cgit v1.2.3 From 6e8878ea74d4b1fca57da6806d530030b345426a Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 24 Dec 2019 00:27:41 +0100 Subject: mod_mam: Log error when unable to delete old messages (fix #1479) [luacheck] --- plugins/mod_mam/mod_mam.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index 2443270f..df5d925a 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -387,6 +387,8 @@ if cleanup_after ~= "never" then if ok then num_users = num_users + 1; sum = sum + (tonumber(ok) or 0); + else + module:log("error", "Could not delete messages for user '%s': %s", user, err); end end module:log("info", "Deleted %d expired messages for %d users", sum, num_users); -- cgit v1.2.3 From 9401b471251fa39b1f127a9a25881a86e9fad776 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 24 Dec 2019 00:27:44 +0100 Subject: mod_mam: Schedule cleanup again if unable to delete messages --- plugins/mod_mam/mod_mam.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index df5d925a..fd319486 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -388,6 +388,7 @@ if cleanup_after ~= "never" then num_users = num_users + 1; sum = sum + (tonumber(ok) or 0); else + cleanup_map:set(cut_off, user, true); module:log("error", "Could not delete messages for user '%s': %s", user, err); end end -- cgit v1.2.3 From 791fa6db10bfed7fb1c3aa5cf0cafa8298ea2e3c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Dec 2019 23:32:49 +0100 Subject: mod_muc_mam: Log error when unable to store stanza (fix #1480) [luacheck] --- plugins/mod_muc_mam.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index a2e3f81b..37e41157 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -357,6 +357,8 @@ local function save_to_history(self, stanza) if id then schedule_cleanup(room_node); stanza:add_direct_child(st.stanza("stanza-id", { xmlns = xmlns_st_id, by = self.jid, id = id })); + else + module:log("error", "Could not archive stanza: %s", err); end end -- cgit v1.2.3 From 26e530afc0ac4935ef0117a2bd5257693f9fdc93 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 24 Dec 2019 00:26:37 +0100 Subject: mod_muc_mam: Log error when unable to delete old messages (fix #1481) [luacheck] --- plugins/mod_muc_mam.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 37e41157..c6104757 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -459,6 +459,8 @@ if cleanup_after ~= "never" then if ok then num_rooms = num_rooms + 1; sum = sum + (tonumber(ok) or 0); + else + module:log("error", "Could not delete messages for room '%s': %s", room, err); end end module:log("info", "Deleted %d expired messages for %d rooms", sum, num_rooms); -- cgit v1.2.3 From b22125f0645b3281d99843dcdb7b44e92b9167cd Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 24 Dec 2019 00:26:40 +0100 Subject: mod_muc_mam: Schedule cleanup again on failure --- plugins/mod_muc_mam.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index c6104757..a9287224 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -460,6 +460,7 @@ if cleanup_after ~= "never" then num_rooms = num_rooms + 1; sum = sum + (tonumber(ok) or 0); else + cleanup_map:set(cut_off, room, true); module:log("error", "Could not delete messages for room '%s': %s", room, err); end end -- cgit v1.2.3 From d04e48f29f39957392861c2132c4727e6cea9e45 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 24 Dec 2019 01:00:35 +0100 Subject: mod_muc_mam: Fix rebase mistake [luacheck] The 'err' was there in trunk --- plugins/mod_muc_mam.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index a9287224..2ce5e1b5 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -352,7 +352,7 @@ local function save_to_history(self, stanza) end -- And stash it - local id = archive:append(room_node, nil, stored_stanza, time_now(), with); + local id, err = archive:append(room_node, nil, stored_stanza, time_now(), with); if id then schedule_cleanup(room_node); -- cgit v1.2.3 From c2267c12ea1c41d430eb5b29645399b7f883db1c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 24 Dec 2019 01:01:05 +0100 Subject: mod_mam: Fix rebase mistake [luacheck] The 'err' was there in trunk --- plugins/mod_mam/mod_mam.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index fd319486..295d90e1 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -297,7 +297,7 @@ local function message_handler(event, c2s) log("debug", "Archiving stanza: %s", stanza:top_tag()); -- And stash it - local ok = archive:append(store_user, nil, clone_for_storage, time_now(), with); + local ok, err = archive:append(store_user, nil, clone_for_storage, time_now(), with); if ok then local clone_for_other_handlers = st.clone(stanza); local id = ok; -- cgit v1.2.3 From 29ebaa995c10b0a9079a6b15aaebbebfbfe89ca0 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 16 Dec 2018 02:56:11 +0100 Subject: core.rostermanager: Cache rosters of offline users for faster access (fixes #1233) (grafted from 42a3e3a2824822cef7640ac56d182c59bdd4224e) --- core/rostermanager.lua | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/core/rostermanager.lua b/core/rostermanager.lua index 61b08002..7bfad0a0 100644 --- a/core/rostermanager.lua +++ b/core/rostermanager.lua @@ -12,6 +12,7 @@ local log = require "util.logger".init("rostermanager"); local new_id = require "util.id".short; +local new_cache = require "util.cache".new; local pairs = pairs; local tostring = tostring; @@ -111,6 +112,23 @@ local function load_roster(username, host) else -- Attempt to load roster for non-loaded user log("debug", "load_roster: loading for offline user: %s", jid); end + local roster_cache = hosts[host] and hosts[host].roster_cache; + if not roster_cache then + if hosts[host] then + roster_cache = new_cache(1024); + hosts[host].roster_cache = roster_cache; + end + else + roster = roster_cache:get(jid); + if roster then + log("debug", "load_roster: cache hit"); + roster_cache:set(jid, roster); + if user then user.roster = roster; end + return roster; + else + log("debug", "load_roster: cache miss, loading from storage"); + end + end local roster_store = storagemanager.open(host, "roster", "keyval"); local data, err = roster_store:get(username); roster = data or {}; @@ -134,6 +152,10 @@ local function load_roster(username, host) if not err then hosts[host].events.fire_event("roster-load", { username = username, host = host, roster = roster }); end + if roster_cache and not user then + log("debug", "load_roster: caching loaded roster"); + roster_cache:set(jid, roster); + end return roster, err; end -- cgit v1.2.3 From e74c062d7ae405a1ba359b26f6594ab03aab3c3e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 31 Dec 2019 02:48:49 +0100 Subject: prosodyctl check: Warn about conflict between mod_pep and mod_pep_simple Related #1483 --- prosodyctl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/prosodyctl b/prosodyctl index a732a73e..3242218d 100755 --- a/prosodyctl +++ b/prosodyctl @@ -937,6 +937,11 @@ function commands.check(arg) print(" Both mod_vcard_legacy and mod_vcard are enabled but they conflict"); print(" with each other. Remove one."); end + if all_modules:contains("pep") and all_modules:contains("pep_simple") then + print(""); + print(" Both mod_pep_simple and mod_pep are enabled but they conflict"); + print(" with each other. Remove one."); + end for host, host_config in pairs(config) do --luacheck: ignore 213/host if type(rawget(host_config, "storage")) == "string" and rawget(host_config, "default_storage") then print(""); -- cgit v1.2.3 From ec0307ecb6909240163cb68b401568326c8a4d37 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 31 Dec 2019 02:50:25 +0100 Subject: prosodyctl: Print a blank line to improve spacing and readability --- prosodyctl | 1 + 1 file changed, 1 insertion(+) diff --git a/prosodyctl b/prosodyctl index 3242218d..982b102e 100755 --- a/prosodyctl +++ b/prosodyctl @@ -934,6 +934,7 @@ function commands.check(arg) end end if all_modules:contains("vcard") and all_modules:contains("vcard_legacy") then + print(""); print(" Both mod_vcard_legacy and mod_vcard are enabled but they conflict"); print(" with each other. Remove one."); end -- cgit v1.2.3 From 954b42849d39da059603d00d1ee8851d25894636 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 2 Jan 2020 10:49:37 +0100 Subject: mod_websocket: Clear mask bit when reflecting ping frames (fixes #1484) --- plugins/mod_websocket.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index 686a8981..6625c676 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -236,6 +236,7 @@ function handle_request(event) return; elseif 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 -- cgit v1.2.3 From 17358273f2d7ad5624459ecc3d0c01b8bb48a1f2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 19 Jan 2020 15:26:22 +0000 Subject: util.startup: Add startup step for parsing command-line options --- util/startup.lua | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/util/startup.lua b/util/startup.lua index e88ed709..fb898970 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -12,6 +12,60 @@ local dependencies = require "util.dependencies"; local original_logging_config; +local short_params = { D = "daemonize", F = "no-daemonize" }; +local value_params = { config = true }; + +function startup.parse_args() + local parsed_opts = {}; + + if #arg > 0 and arg[1] ~= "--config" then + while true do + local raw_param = arg[1]; + if not raw_param then + break; + end + + local prefix = raw_param:match("^%-%-?"); + if not prefix then + break; + elseif prefix == "--" and raw_param == "--" then + table.remove(arg, 1); + break; + end + local param = table.remove(arg, 1):sub(#prefix+1); + if #param == 1 then + param = short_params[param]; + end + + if not param then + print("Unknown command-line option: "..tostring(param)); + print("Perhaps you meant to use prosodyctl instead?"); + os.exit(1); + end + + local param_k, param_v; + if value_params[param] then + param_k, param_v = param, table.remove(arg, 1); + if not param_v then + print("Expected a value to follow command-line option: "..raw_param); + os.exit(1); + end + else + param_k, param_v = param:match("^([^=]+)=(.+)$"); + if not param_k then + if param:match("^no%-") then + param_k, param_v = param:sub(4), false; + else + param_k, param_v = param, true; + end + end + end + parsed_opts[param_k] = param_v; + end + end + prosody.opts = parsed_opts; +end + function startup.read_config() local filenames = {}; -- cgit v1.2.3 From f24308103fa0d4057a36069dffff7514760274e5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 19 Jan 2020 15:27:16 +0000 Subject: prosody/util.startup: Switch to parse_args() for --root and --config --- prosody | 6 +----- util/startup.lua | 12 ++++++------ 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/prosody b/prosody index 204fb36d..3659ca55 100755 --- a/prosody +++ b/prosody @@ -43,11 +43,7 @@ if CFG_DATADIR then end end -if #arg > 0 and arg[1] ~= "--config" then - print("Unknown command-line option: "..tostring(arg[1])); - print("Perhaps you meant to use prosodyctl instead?"); - return 1; -end + local startup = require "util.startup"; local async = require "util.async"; diff --git a/util/startup.lua b/util/startup.lua index fb898970..93f40c8f 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -70,12 +70,11 @@ function startup.read_config() local filenames = {}; local filename; - if arg[1] == "--config" and arg[2] then - table.insert(filenames, arg[2]); + if prosody.opts.config then + table.insert(filenames, prosody.opts.config); if CFG_CONFIGDIR then - table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]); + table.insert(filenames, CFG_CONFIGDIR.."/"..prosody.opts.config); end - table.remove(arg, 1); table.remove(arg, 1); elseif os.getenv("PROSODY_CONFIG") then -- Passed by prosodyctl table.insert(filenames, os.getenv("PROSODY_CONFIG")); else @@ -459,8 +458,7 @@ function startup.switch_user() os.exit(1); end prosody.current_uid = pposix.getuid(); - local arg_root = arg[1] == "--root"; - if arg_root then table.remove(arg, 1); end + local arg_root = prosody.opts.root; if prosody.current_uid == 0 and config.get("*", "run_as_root") ~= true and not arg_root then -- We haz root! local desired_user = config.get("*", "prosody_user") or "prosody"; @@ -569,6 +567,7 @@ end -- prosodyctl only function startup.prosodyctl() + startup.parse_args(); startup.init_global_state(); startup.read_config(); startup.force_console_logging(); @@ -589,6 +588,7 @@ end function startup.prosody() -- These actions are in a strict order, as many depend on -- previous steps to have already been performed + startup.parse_args(); startup.init_global_state(); startup.read_config(); startup.init_logging(); -- cgit v1.2.3 From 37ba5f795f519c55a28ae6b2a7a8cb239d097545 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 19 Jan 2020 15:28:09 +0000 Subject: mod_posix: Support for command-line flags to override 'daemonize' config option -D / --daemonize -F / --no-daemonize --- plugins/mod_posix.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/mod_posix.lua b/plugins/mod_posix.lua index 23df4d23..fe826c22 100644 --- a/plugins/mod_posix.lua +++ b/plugins/mod_posix.lua @@ -126,7 +126,12 @@ function syslog_sink_maker(config) -- luacheck: ignore 212/config end require "core.loggingmanager".register_sink_type("syslog", syslog_sink_maker); -local daemonize = module:get_option("daemonize", prosody.installed); +local daemonize = prosody.opts.daemonize; + +if daemonize == nil then + -- Fall back to config file if not specified on command-line + daemonize = module:get_option("daemonize", prosody.installed); +end local function remove_log_sinks() local lm = require "core.loggingmanager"; -- cgit v1.2.3 From 8b60eaf2f346875a10a206088f6214f2200dcc55 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 19 Jan 2020 15:39:13 +0000 Subject: util.startup: Fix logic to make --config work again --- util/startup.lua | 76 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/util/startup.lua b/util/startup.lua index 93f40c8f..e54d6935 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -18,50 +18,52 @@ local value_params = { config = true }; function startup.parse_args() local parsed_opts = {}; - if #arg > 0 and arg[1] ~= "--config" then - while true do - local raw_param = arg[1]; - if not raw_param then - break; - end + if #arg == 0 then + return; + end + while true do + local raw_param = arg[1]; + if not raw_param then + break; + end - local prefix = raw_param:match("^%-%-?"); - if not prefix then - break; - elseif prefix == "--" and raw_param == "--" then - table.remove(arg, 1); - break; - end - local param = table.remove(arg, 1):sub(#prefix+1); - if #param == 1 then - param = short_params[param]; - end + local prefix = raw_param:match("^%-%-?"); + if not prefix then + break; + elseif prefix == "--" and raw_param == "--" then + table.remove(arg, 1); + break; + end + local param = table.remove(arg, 1):sub(#prefix+1); + if #param == 1 then + param = short_params[param]; + end - if not param then - print("Unknown command-line option: "..tostring(param)); - print("Perhaps you meant to use prosodyctl instead?"); + if not param then + print("Unknown command-line option: "..tostring(param)); + print("Perhaps you meant to use prosodyctl instead?"); + os.exit(1); + end + + local param_k, param_v; + if value_params[param] then + param_k, param_v = param, table.remove(arg, 1); + if not param_v then + print("Expected a value to follow command-line option: "..raw_param); os.exit(1); end - - local param_k, param_v; - if value_params[param] then - param_k, param_v = param, table.remove(arg, 1); - if not param_v then - print("Expected a value to follow command-line option: "..raw_param); - os.exit(1); - end - else - param_k, param_v = param:match("^([^=]+)=(.+)$"); - if not param_k then - if param:match("^no%-") then - param_k, param_v = param:sub(4), false; - else - param_k, param_v = param, true; - end + else + param_k, param_v = param:match("^([^=]+)=(.+)$"); + if not param_k then + if param:match("^no%-") then + param_k, param_v = param:sub(4), false; + else + param_k, param_v = param, true; end end - parsed_opts[param_k] = param_v; end + parsed_opts[param_k] = param_v; + print("ARG", param_k, param_v); end prosody.opts = parsed_opts; end -- cgit v1.2.3 From a443414b2b1582836126d7c3b09957cdd1cd692e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 19 Jan 2020 15:50:16 +0000 Subject: util.startup: Remove accidentally-committed debugging --- util/startup.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/util/startup.lua b/util/startup.lua index e54d6935..35be3162 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -63,7 +63,6 @@ function startup.parse_args() end end parsed_opts[param_k] = param_v; - print("ARG", param_k, param_v); end prosody.opts = parsed_opts; end -- cgit v1.2.3 From 0b1e6fe7a719abfda49ca17413c6a567c90635a2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 19 Jan 2020 15:50:32 +0000 Subject: util.startup: Ensure prosody.opts exists even when no options provided --- util/startup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/startup.lua b/util/startup.lua index 35be3162..24ed6026 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -17,6 +17,7 @@ local value_params = { config = true }; function startup.parse_args() local parsed_opts = {}; + prosody.opts = parsed_opts; if #arg == 0 then return; @@ -64,7 +65,6 @@ function startup.parse_args() end parsed_opts[param_k] = param_v; end - prosody.opts = parsed_opts; end function startup.read_config() -- cgit v1.2.3 -- cgit v1.2.3 From cd4fcca5aa16fedd7a5cbe42cd6e78ccade1ec54 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Jan 2020 21:43:36 +0000 Subject: Log warning when using prosodyctl start/stop/restart --- prosodyctl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/prosodyctl b/prosodyctl index 982b102e..b16d9230 100755 --- a/prosodyctl +++ b/prosodyctl @@ -198,11 +198,25 @@ function commands.deluser(arg) return 1; end +local function service_command_warning(command) + if prosody.installed and configmanager.get("*", "prosodyctl_service_warnings") ~= false then + show_warning("WARNING: Use of prosodyctl start/stop/restart/reload is not recommended"); + show_warning(" if Prosody is managed by an init system - use that directly instead."); + if lfs.attributes("/etc/systemd") then + show_warning(" e.g. systemctl %s prosody", command); + elseif lfs.attributes("/etc/init.d/prosody") then + show_warning(" e.g. /etc/init.d/prosody %s", command); + end + show_warning(""); + end +end + function commands.start(arg) if arg[1] == "--help" then show_usage([[start]], [[Start Prosody]]); return 1; end + service_command_warning("start"); local ok, ret = prosodyctl.isrunning(); if not ok then show_message(error_messages[ret]); @@ -293,6 +307,8 @@ function commands.stop(arg) return 1; end + service_command_warning("stop"); + if not prosodyctl.isrunning() then show_message("Prosody is not running"); return 1; @@ -328,6 +344,8 @@ function commands.restart(arg) return 1; end + service_command_warning("restart"); + commands.stop(arg); return commands.start(arg); end @@ -421,6 +439,8 @@ function commands.reload(arg) return 1; end + service_command_warning("reload"); + if not prosodyctl.isrunning() then show_message("Prosody is not running"); return 1; -- cgit v1.2.3 From 8a032412450f7dabaed77359dac45d4450662951 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Jan 2020 21:59:13 +0000 Subject: prosodyctl: Fix some luacheck warnings --- prosodyctl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/prosodyctl b/prosodyctl index b16d9230..028e08d8 100755 --- a/prosodyctl +++ b/prosodyctl @@ -69,6 +69,7 @@ local modulemanager = require "core.modulemanager" local prosodyctl = require "util.prosodyctl" local socket = require "socket" local dependencies = require "util.dependencies"; +local lfs = dependencies.softreq "lfs"; ----------------------- @@ -198,14 +199,15 @@ function commands.deluser(arg) return 1; end -local function service_command_warning(command) +local function service_command_warning(service_command) if prosody.installed and configmanager.get("*", "prosodyctl_service_warnings") ~= false then show_warning("WARNING: Use of prosodyctl start/stop/restart/reload is not recommended"); show_warning(" if Prosody is managed by an init system - use that directly instead."); + lfs = lfs or require if lfs.attributes("/etc/systemd") then - show_warning(" e.g. systemctl %s prosody", command); + show_warning(" e.g. systemctl %s prosody", service_command); elseif lfs.attributes("/etc/init.d/prosody") then - show_warning(" e.g. /etc/init.d/prosody %s", command); + show_warning(" e.g. /etc/init.d/prosody %s", service_command); end show_warning(""); end @@ -512,7 +514,6 @@ function commands.unregister(arg) end local openssl; -local lfs; local cert_commands = {}; -- cgit v1.2.3 From 70e9ff3887f78b7ce8ea5487c5c7b9bdc128a7d9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 24 Jan 2020 16:21:30 +0000 Subject: mod_websocket: Fire event on session creation (thanks Aaron van Meerten) --- plugins/mod_websocket.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index 6625c676..177259e6 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -305,6 +305,8 @@ function handle_request(event) response.headers.sec_webSocket_accept = base64(sha1(request.headers.sec_websocket_key .. "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")); response.headers.sec_webSocket_protocol = "xmpp"; + module:fire_event("websocket-session", { session = session, request = request }); + session.log("debug", "Sending WebSocket handshake"); return ""; -- cgit v1.2.3 From c17d65758ed1b357c1b3cf2a8e4af773f301c2cd Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 24 Jan 2020 23:27:49 +0100 Subject: mod_admin_telnet: Create a DNS resolver per console session (fixes #1492) This is now the common pattern, eg see mod_s2s. --- plugins/mod_admin_telnet.lua | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index 1cbe27a4..59eca28b 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -1074,36 +1074,49 @@ end def_env.dns = {}; local adns = require"net.adns"; -local dns = require"net.dns"; + +local function get_resolver(session) + local resolver = session.dns_resolver; + if not resolver then + resolver = adns.resolver(); + session.dns_resolver = resolver; + end + return resolver; +end function def_env.dns:lookup(name, typ, class) + local resolver = get_resolver(self.session); local ret = "Query sent"; local print = self.session.print; local function handler(...) ret = "Got response"; print(...); end - adns.lookup(handler, name, typ, class); + resolver:lookup(handler, name, typ, class); return true, ret; end function def_env.dns:addnameserver(...) - dns._resolver:addnameserver(...) + local resolver = get_resolver(self.session); + resolver._resolver:addnameserver(...) return true end function def_env.dns:setnameserver(...) - dns._resolver:setnameserver(...) + local resolver = get_resolver(self.session); + resolver._resolver:setnameserver(...) return true end function def_env.dns:purge() - dns.purge() + local resolver = get_resolver(self.session); + resolver._resolver:purge() return true end function def_env.dns:cache() - return true, "Cache:\n"..tostring(dns.cache()) + local resolver = get_resolver(self.session); + return true, "Cache:\n"..tostring(resolver._resolver.cache) end def_env.http = {}; -- cgit v1.2.3 From 239bd598467f2e4b2768a71a2b2fe708f34181a3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 13 Feb 2020 14:27:12 +0000 Subject: mod_muc: Allow control over the server-admins-are-room-owners feature (see #1174) --- plugins/muc/mod_muc.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua index 954bae92..b9c7c859 100644 --- a/plugins/muc/mod_muc.lua +++ b/plugins/muc/mod_muc.lua @@ -104,7 +104,8 @@ local function is_admin(jid) return um_is_admin(jid, module.host); end -do -- Monkey patch to make server admins room owners +if module:get_option_boolean("component_admins_as_room_owners", true) then + -- Monkey patch to make server admins room owners local _get_affiliation = room_mt.get_affiliation; function room_mt:get_affiliation(jid) if is_admin(jid) then return "owner"; end -- cgit v1.2.3 From a8812b45ad23d58ba7a9da7d3b341d46bb006df4 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 21 Feb 2020 23:00:44 +0100 Subject: mod_storage_sql: Fix check for deletion limits (fixes #1494) The check was only performed if sql_manage_tables was set to true (the default) It should always be performed --- plugins/mod_storage_sql.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua index a449091e..a9298138 100644 --- a/plugins/mod_storage_sql.lua +++ b/plugins/mod_storage_sql.lua @@ -622,15 +622,15 @@ function module.load() module:log("error", "Old database format detected. Please run: prosodyctl mod_%s upgrade", module.name); return false, "database upgrade needed"; end - if engine.params.driver == "SQLite3" then - for row in engine:select("PRAGMA compile_options") do - if row[1] == "ENABLE_UPDATE_DELETE_LIMIT" then - engine._have_delete_limit = true; - end - end - end end end); + if engine.params.driver == "SQLite3" then + for row in engine:select("PRAGMA compile_options") do + if row[1] == "ENABLE_UPDATE_DELETE_LIMIT" then + engine._have_delete_limit = true; + end + end + end engines[sql.db2uri(params)] = engine; end -- cgit v1.2.3 From e69d6a21b3a1e0af470eb7a88239b20d463e123c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 22 Feb 2020 16:23:43 +0100 Subject: mod_storage_sql: Move update limit check back one step, fixes traceback 0c00274528a4 moved it one step too far, so the check was performed before connecting to the database instead of after. --- plugins/mod_storage_sql.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua index a9298138..73207073 100644 --- a/plugins/mod_storage_sql.lua +++ b/plugins/mod_storage_sql.lua @@ -623,14 +623,14 @@ function module.load() return false, "database upgrade needed"; end end - end); - if engine.params.driver == "SQLite3" then - for row in engine:select("PRAGMA compile_options") do - if row[1] == "ENABLE_UPDATE_DELETE_LIMIT" then - engine._have_delete_limit = true; + if engine.params.driver == "SQLite3" then + for row in engine:select("PRAGMA compile_options") do + if row[1] == "ENABLE_UPDATE_DELETE_LIMIT" then + engine._have_delete_limit = true; + end end end - end + end); engines[sql.db2uri(params)] = engine; end -- cgit v1.2.3 From 81325abb7a06ca38e273eef6b0db7f4750c82bcb Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 24 Feb 2020 23:16:08 +0100 Subject: util.datamanager: Fix iterating over "users" (thanks marc0s) The 'store' path componend used to be unescaped until 756a2a00e7e7 added escaping to address issues with characters like '/' used in PEP, but with a special case for '_' which was already in common use in 'store' path components. Missed adding this escaping here. --- util/datamanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/datamanager.lua b/util/datamanager.lua index cf96887b..0d7060b7 100644 --- a/util/datamanager.lua +++ b/util/datamanager.lua @@ -320,7 +320,7 @@ local type_map = { local function users(host, store, typ) -- luacheck: ignore 431/store typ = type_map[typ or "keyval"]; - local store_dir = format("%s/%s/%s", data_path, encode(host), store); + local store_dir = format("%s/%s/%s", data_path, encode(host), store_encode(store)); local mode, err = lfs.attributes(store_dir, "mode"); if not mode then -- cgit v1.2.3 From ea1ca4fd42978b1606f64dd3d32818f3ee8166a6 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 27 Feb 2020 19:42:51 +0100 Subject: mod_muc_mam: Fix traceback saving message from non-occupant (fixes #1497) Caused by a plugin sending a message from the room itself. --- plugins/mod_muc_mam.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 2ce5e1b5..f7ea3136 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -335,11 +335,14 @@ local function save_to_history(self, stanza) if stanza.name == "message" and self:get_whois() == "anyone" then stored_stanza = st.clone(stanza); stored_stanza.attr.to = nil; - local actor = jid_bare(self._occupants[stanza.attr.from].jid); - local affiliation = self:get_affiliation(actor) or "none"; - local role = self:get_role(actor) or self:get_default_role(affiliation); - stored_stanza:add_direct_child(st.stanza("x", { xmlns = xmlns_muc_user }) - :tag("item", { affiliation = affiliation; role = role; jid = actor })); + local occupant = self._occupants[stanza.attr.from]; + if occupant then + local actor = jid_bare(occupant.jid); + local affiliation = self:get_affiliation(actor) or "none"; + local role = self:get_role(actor) or self:get_default_role(affiliation); + stored_stanza:add_direct_child(st.stanza("x", { xmlns = xmlns_muc_user }) + :tag("item", { affiliation = affiliation; role = role; jid = actor })); + end end -- Policy check -- cgit v1.2.3 From 3ab4eb0e103c29ab4e0f82ba6c28002052e5a0b8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 27 Feb 2020 20:01:54 +0100 Subject: mod_pubsub, mod_pep: Ensure correct number of children of (fixes #1496) --- plugins/mod_pep.lua | 2 +- plugins/mod_pubsub/mod_pubsub.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_pep.lua b/plugins/mod_pep.lua index 12be41a2..f8047a92 100644 --- a/plugins/mod_pep.lua +++ b/plugins/mod_pep.lua @@ -51,7 +51,7 @@ function module.restore(data) end function is_item_stanza(item) - return st.is_stanza(item) and item.attr.xmlns == xmlns_pubsub and item.name == "item"; + return st.is_stanza(item) and item.attr.xmlns == xmlns_pubsub and item.name == "item" and #item.tags == 1; end function check_node_config(node, actor, new_config) -- luacheck: ignore 212/node 212/actor diff --git a/plugins/mod_pubsub/mod_pubsub.lua b/plugins/mod_pubsub/mod_pubsub.lua index 855c5fd2..4276bc5e 100644 --- a/plugins/mod_pubsub/mod_pubsub.lua +++ b/plugins/mod_pubsub/mod_pubsub.lua @@ -112,7 +112,7 @@ function check_node_config(node, actor, new_config) -- luacheck: ignore 212/acto end function is_item_stanza(item) - return st.is_stanza(item) and item.attr.xmlns == xmlns_pubsub and item.name == "item"; + return st.is_stanza(item) and item.attr.xmlns == xmlns_pubsub and item.name == "item" and #item.tags == 1; end module:hook("pubsub-summary/http://www.w3.org/2005/Atom", function (event) -- cgit v1.2.3 From 66b1c21e0a25adb032f579a4334fe78c5aed29fe Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 11 Mar 2020 18:07:03 +0100 Subject: net.server_epoll: Fix indentation Some lines seem to have gotten the wrong indentation, possibly caused by Meld which often ignores lines with only whitespace changes and leaves their previous indentation. --- net/server_epoll.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/server_epoll.lua b/net/server_epoll.lua index 0c03ae15..2182d56a 100644 --- a/net/server_epoll.lua +++ b/net/server_epoll.lua @@ -102,7 +102,7 @@ local function runtimers(next_delay, min_wait) if peek > now then next_delay = peek - now; break; - end + end local _, timer, id = timers:pop(); local ok, ret = pcall(timer[2], now); @@ -110,10 +110,10 @@ local function runtimers(next_delay, min_wait) local next_time = now+ret; timer[1] = next_time; timers:insert(timer, next_time); - end + end peek = timers:peek(); - end + end if peek == nil then return next_delay; end -- cgit v1.2.3 From 02b23bdc6699dfcd5f14de1933a171b6415a64a5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 11 Mar 2020 21:15:01 +0100 Subject: mod_mam,mod_muc_mam: Allow other work to be performed during archive cleanup (fixes #1504) This lets Prosody handle socket related work between each step in the cleanup in order to prevent the server from being completely blocked during this. An async storage backend would not need this but those are currently rare. --- plugins/mod_mam/mod_mam.lua | 6 +++++- plugins/mod_muc_mam.lua | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index 295d90e1..e7d89a95 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -362,7 +362,8 @@ if cleanup_after ~= "never" then end end - cleanup_runner = require "util.async".runner(function () + local async = require "util.async"; + cleanup_runner = async.runner(function () local users = {}; local cut_off = datestamp(os.time() - cleanup_after); for date in cleanup_storage:users() do @@ -391,6 +392,9 @@ if cleanup_after ~= "never" then cleanup_map:set(cut_off, user, true); module:log("error", "Could not delete messages for user '%s': %s", user, err); end + local wait, done = async.waiter(); + module:add_timer(0.01, done); + wait(); end module:log("info", "Deleted %d expired messages for %d users", sum, num_users); end); diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index f7ea3136..5a01324d 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -437,7 +437,8 @@ if cleanup_after ~= "never" then end end - cleanup_runner = require "util.async".runner(function () + local async = require "util.async"; + cleanup_runner = async.runner(function () local rooms = {}; local cut_off = datestamp(os.time() - cleanup_after); for date in cleanup_storage:users() do @@ -466,6 +467,9 @@ if cleanup_after ~= "never" then cleanup_map:set(cut_off, room, true); module:log("error", "Could not delete messages for room '%s': %s", room, err); end + local wait, done = async.waiter(); + module:add_timer(0.01, done); + wait(); end module:log("info", "Deleted %d expired messages for %d rooms", sum, num_rooms); end); -- cgit v1.2.3 From e50a3fac0be9cee64746f88dda46989ec87e8e29 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 12 Mar 2020 20:32:07 +0000 Subject: MUC: Persist affiliation_data in new MUC format! --- plugins/muc/muc.lib.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 639ecc38..bc370da5 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1449,6 +1449,7 @@ function room_mt:freeze(live) frozen = { _jid = self.jid; _data = self._data; + _affiliation_data = self._affiliation_data; }; for user, affiliation in pairs(self._affiliations) do frozen[user] = affiliation; -- cgit v1.2.3 From af11cae5114d8237c8f17be3a89482bc70cf42b8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 22 Mar 2020 17:35:26 +0100 Subject: mod_admin_telnet: Handle unavailable cipher info (fixes #1510) The LuaSec :info() method gathers info using the OpenSSL function SSL_get_current_cipher(). Documentation for this function states that it may return NULL if no session has been established (yet). If so, the LuaSec functions wrapping this return nil, triggering a nil-indexing error in mod_admin_telnet. --- plugins/mod_admin_telnet.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index 59eca28b..b0e349da 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -528,11 +528,14 @@ local function tls_info(session, line) common_info(session, line); if session.secure then local sock = session.conn and session.conn.socket and session.conn:socket(); - if sock and sock.info then - local info = sock:info(); - line[#line+1] = ("(%s with %s)"):format(info.protocol, info.cipher); - else - line[#line+1] = "(cipher info unavailable)"; + if sock then + local info = sock.info and sock:info(); + if info then + line[#line+1] = ("(%s with %s)"):format(info.protocol, info.cipher); + else + -- TLS session might not be ready yet + line[#line+1] = "(cipher info unavailable)"; + end end else line[#line+1] = "(insecure)"; -- cgit v1.2.3 From 51f4e1c896e2135b3164847c37d8d2410a9127a4 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 22 Mar 2020 21:05:59 +0100 Subject: mod_storage_sql: Add index covering sort_id to improve performance (fixes #1505) --- plugins/mod_storage_sql.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua index 73207073..5b1c3603 100644 --- a/plugins/mod_storage_sql.lua +++ b/plugins/mod_storage_sql.lua @@ -500,6 +500,7 @@ local function create_table(engine) -- luacheck: ignore 431/engine Index { name="prosodyarchive_index", unique = engine.params.driver ~= "MySQL", "host", "user", "store", "key" }; Index { name="prosodyarchive_with_when", "host", "user", "store", "with", "when" }; Index { name="prosodyarchive_when", "host", "user", "store", "when" }; + Index { name="prosodyarchive_sort", "host", "user", "store", "sort_id" }; }; engine:transaction(function() ProsodyArchiveTable:create(engine); -- cgit v1.2.3 From 1ee06a447b217fd288654e65236bd81b65a640f5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 29 Sep 2019 15:05:17 +0200 Subject: mod_register_ibr: Add FORM_TYPE as required by XEP-0077 (fixes #1511) Backport of f90abf142d53 from trunk --- plugins/mod_register_ibr.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_register_ibr.lua b/plugins/mod_register_ibr.lua index bbe7581d..e04e6ecd 100644 --- a/plugins/mod_register_ibr.lua +++ b/plugins/mod_register_ibr.lua @@ -25,6 +25,7 @@ end); local account_details = module:open_store("account_details"); local field_map = { + FORM_TYPE = { name = "FORM_TYPE", type = "hidden", value = "jabber:iq:register" }; username = { name = "username", type = "text-single", label = "Username", required = true }; password = { name = "password", type = "text-private", label = "Password", required = true }; nick = { name = "nick", type = "text-single", label = "Nickname" }; @@ -50,6 +51,7 @@ local registration_form = dataform_new{ title = title; instructions = instructions; + field_map.FORM_TYPE; field_map.username; field_map.password; }; -- cgit v1.2.3 From b60c0cb4354cbbf4904ed9c774d9522f8d628085 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 3 Apr 2020 22:35:47 +0200 Subject: mod_vcard4: Report correct error condition (fixes #1521) On error, the second return value is the error condition, not the third. --- plugins/mod_vcard4.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_vcard4.lua b/plugins/mod_vcard4.lua index 378c0602..04dbca9e 100644 --- a/plugins/mod_vcard4.lua +++ b/plugins/mod_vcard4.lua @@ -14,9 +14,9 @@ module:hook("iq-get/bare/urn:ietf:params:xml:ns:vcard-4.0:vcard", function (even local ok, id, item = pep_service:get_last_item("urn:xmpp:vcard4", stanza.attr.from); if ok and item then origin.send(st.reply(stanza):add_child(item.tags[1])); - elseif item == "item-not-found" or not id then + elseif id == "item-not-found" or not id then origin.send(st.error_reply(stanza, "cancel", "item-not-found")); - elseif item == "forbidden" then + elseif id == "forbidden" then origin.send(st.error_reply(stanza, "auth", "forbidden")); else origin.send(st.error_reply(stanza, "modify", "undefined-condition")); -- cgit v1.2.3 From ac080de447b2b400dadc78a7e550d5e8053d6790 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 10 Apr 2020 16:11:09 +0200 Subject: core.certmanager: Look for privkey.pem to go with fullchain.pem (fix #1526) This makes `prosodyctl cert import example.com /path/to/example.com/fullchain.pem` work. This was never intended to, yet users commonly tried this and got problems. --- core/certmanager.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/certmanager.lua b/core/certmanager.lua index 5282a6f5..40021db6 100644 --- a/core/certmanager.lua +++ b/core/certmanager.lua @@ -73,13 +73,15 @@ local function find_cert(user_certs, name) local key_path = certs .. key_try[i]:format(name); if stat(crt_path, "mode") == "file" then - if key_path:sub(-4) == ".crt" then - key_path = key_path:sub(1, -4) .. "key"; - if stat(key_path, "mode") == "file" then - log("debug", "Selecting certificate %s with key %s for %s", crt_path, key_path, name); - return { certificate = crt_path, key = key_path }; + if crt_path == key_path then + if key_path:sub(-4) == ".crt" then + key_path = key_path:sub(1, -4) .. "key"; + elseif key_path:sub(-13) == "fullchain.pem" then + key_path = key_path:sub(1, -14) .. "privkey.pem"; end - elseif stat(key_path, "mode") == "file" then + end + + if stat(key_path, "mode") == "file" then log("debug", "Selecting certificate %s with key %s for %s", crt_path, key_path, name); return { certificate = crt_path, key = key_path }; end -- cgit v1.2.3 From 0b713578627418a5bc100d83253bb84b365fef3d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 26 Jan 2020 16:40:21 +0100 Subject: util.prosodyctl: Tell prosody do daemonize via command line flag (fixes #1514) Backport of 88be11e9f9b9 --- util/prosodyctl.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 5f0c4d12..6c84ab6e 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -238,9 +238,9 @@ local function start(source_dir) return false, "already-running"; end if not source_dir then - os.execute("./prosody"); + os.execute("./prosody -D"); else - os.execute(source_dir.."/../../bin/prosody"); + os.execute(source_dir.."/../../bin/prosody -D"); end return true; end -- cgit v1.2.3 From 524ccf5c3f5457fe339a2c8b8a748ee5632dba9a Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 25 Aug 2019 20:22:35 +0200 Subject: core.certmanager: Move EECDH ciphers before EDH in default cipherstring (fixes #1513) Backport of 94e341dee51c The original intent of having kEDH before kEECDH was that if a `dhparam` file was specified, this would be interpreted as a preference by the admin for old and well-tested Diffie-Hellman key agreement over newer elliptic curve ones. Otherwise the faster elliptic curve ciphersuites would be preferred. This didn't really work as intended since this affects the ClientHello on outgoing s2s connections, leading to some servers using poorly configured kEDH. With Debian shipping OpenSSL settings that enforce a higher security level, this caused interoperability problems with servers that use DH params smaller than 2048 bits. E.g. jabber.org at the time of this writing has 1024 bit DH params. MattJ says > Curves have won, and OpenSSL is less weird about them now --- core/certmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/certmanager.lua b/core/certmanager.lua index 40021db6..20b91318 100644 --- a/core/certmanager.lua +++ b/core/certmanager.lua @@ -125,8 +125,8 @@ local core_defaults = { "P-521", }; ciphers = { -- Enabled ciphers in order of preference: - "HIGH+kEDH", -- Ephemeral Diffie-Hellman key exchange, if a 'dhparam' file is set "HIGH+kEECDH", -- Ephemeral Elliptic curve Diffie-Hellman key exchange + "HIGH+kEDH", -- Ephemeral Diffie-Hellman key exchange, if a 'dhparam' file is set "HIGH", -- Other "High strength" ciphers -- Disabled cipher suites: "!PSK", -- Pre-Shared Key - not used for XMPP -- cgit v1.2.3 From 5e262dfdf6bdc94ac8a1b49c8c73124a90c6d1df Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Apr 2020 13:16:25 +0100 Subject: MUC: Always include 'affiliation'/'role' attributes, defaulting to 'none' if nil --- plugins/muc/muc.lib.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index bc370da5..93334b88 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -143,7 +143,7 @@ end -- actor is the attribute table local function add_item(x, affiliation, role, jid, nick, actor_nick, actor_jid, reason) - x:tag("item", {affiliation = affiliation; role = role; jid = jid; nick = nick;}) + x:tag("item", {affiliation = affiliation or "none"; role = role; jid = jid; nick = nick;}) if actor_nick or actor_jid then x:tag("actor", {nick = actor_nick; jid = actor_jid;}):up() end -- cgit v1.2.3 From 262d16c2a679f01a1d712d3724bc28715c3daeb9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Apr 2020 13:29:23 +0100 Subject: MUC tests: Add to expected form field --- spec/scansion/muc_register.scs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/scansion/muc_register.scs b/spec/scansion/muc_register.scs index e1eaf4e0..a077cd76 100644 --- a/spec/scansion/muc_register.scs +++ b/spec/scansion/muc_register.scs @@ -100,7 +100,9 @@ Juliet receives: http://jabber.org/protocol/muc#register - + + + @@ -339,7 +341,9 @@ Romeo receives: http://jabber.org/protocol/muc#register - + + + -- cgit v1.2.3 From c4aebb62ea74ed42c4add8c9db1436a6326bdcd0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Apr 2020 13:43:29 +0100 Subject: Backed out changeset 18f2c7bc5795 (was testing against wrong branch) --- spec/scansion/muc_register.scs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/spec/scansion/muc_register.scs b/spec/scansion/muc_register.scs index a077cd76..e1eaf4e0 100644 --- a/spec/scansion/muc_register.scs +++ b/spec/scansion/muc_register.scs @@ -100,9 +100,7 @@ Juliet receives: http://jabber.org/protocol/muc#register - - - + @@ -341,9 +339,7 @@ Romeo receives: http://jabber.org/protocol/muc#register - - - + -- cgit v1.2.3 From 68aedef579d411aad1a1c81b4b32ad5246c1143b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Apr 2020 13:44:47 +0100 Subject: MUC tests: Add missing affiliation attribute --- spec/scansion/muc_register.scs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/scansion/muc_register.scs b/spec/scansion/muc_register.scs index e1eaf4e0..a7e57177 100644 --- a/spec/scansion/muc_register.scs +++ b/spec/scansion/muc_register.scs @@ -198,7 +198,7 @@ Juliet receives: Romeo receives: - + -- cgit v1.2.3 From 2d57486958db8f1d07b7afd19340ece0a1a8714d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 25 Apr 2020 20:55:00 +0200 Subject: mod_http_files: Avoid using inode in etag, fix #1498 --- plugins/mod_http_files.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_http_files.lua b/plugins/mod_http_files.lua index 1dae0d6d..a8398c01 100644 --- a/plugins/mod_http_files.lua +++ b/plugins/mod_http_files.lua @@ -112,7 +112,7 @@ function serve(opts) local last_modified = os_date('!%a, %d %b %Y %H:%M:%S GMT', attr.modification); response_headers.last_modified = last_modified; - local etag = ('"%02x-%x-%x-%x"'):format(attr.dev or 0, attr.ino or 0, attr.size or 0, attr.modification or 0); + local etag = ('"%x-%x-%x"'):format(attr.change or 0, attr.size or 0, attr.modification or 0); response_headers.etag = etag; local if_none_match = request_headers.if_none_match -- cgit v1.2.3 From 75c5a894fc8949106b13a01918a25bd8dc5c82c3 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 26 Apr 2020 20:39:39 +0200 Subject: mod_carbons: Fix handling of incoming MUC PMs #1540 27f5db07bec9 fixed this wrong. The code is supposed to check if the stanza is NOT sent to your bare JID. A MUC PM is always sent to your full JID. Hopefully nobody sends MUC invites to full JIDs, because those would be skipped by this as well. --- plugins/mod_carbons.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_carbons.lua b/plugins/mod_carbons.lua index 1dcd4a07..79d3e737 100644 --- a/plugins/mod_carbons.lua +++ b/plugins/mod_carbons.lua @@ -69,7 +69,7 @@ local function message_handler(event, c2s) elseif stanza:get_child("no-copy", "urn:xmpp:hints") then module:log("debug", "Message has no-copy hint, ignoring"); return - elseif not c2s and bare_jid == orig_from and stanza:get_child("x", "http://jabber.org/protocol/muc#user") then + elseif not c2s and bare_jid ~= orig_to and stanza:get_child("x", "http://jabber.org/protocol/muc#user") then module:log("debug", "MUC PM, ignoring"); return end -- cgit v1.2.3 From 8882e11c711176dbe1fd1133e5d8111eb5f06ef3 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 26 Apr 2020 20:58:51 +0200 Subject: mod_tls: Log when certificates are (re)loaded Meant to reduce user confusion over what's reloaded and not. --- plugins/mod_tls.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/mod_tls.lua b/plugins/mod_tls.lua index eb208e28..8c0c4e20 100644 --- a/plugins/mod_tls.lua +++ b/plugins/mod_tls.lua @@ -36,7 +36,7 @@ local host = hosts[module.host]; local ssl_ctx_c2s, ssl_ctx_s2sout, ssl_ctx_s2sin; local ssl_cfg_c2s, ssl_cfg_s2sout, ssl_cfg_s2sin; -function module.load() +function module.load(reload) local NULL, err = {}; local modhost = module.host; local parent = modhost:match("%.(.*)$"); @@ -63,6 +63,12 @@ function module.load() module:log("debug", "Creating context for s2sin"); ssl_ctx_s2sin, err, ssl_cfg_s2sin = create_context(host.host, "server", host_s2s, host_ssl, global_s2s); -- for incoming server connections if not ssl_ctx_s2sin then module:log("error", "Error creating contexts for s2sin: %s", err); end + + if reload then + module:log("info", "Certificates reloaded"); + else + module:log("info", "Certificates loaded"); + end end module:hook_global("config-reloaded", module.load); -- cgit v1.2.3 From ee464bb36d566f5f47e0ab3d3ba562f227fb47bd Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 29 Apr 2020 18:04:05 +0200 Subject: mod_muc_mam: Fix stanza id filter event name, fixes #1546 Nice typo --- plugins/mod_muc_mam.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 5a01324d..8216e2e6 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -309,7 +309,7 @@ module:hook("muc-get-history", function (event) return true; end, 1); -module:hook("muc-broadcast-messages", function (event) +module:hook("muc-broadcast-message", function (event) local room, stanza = event.room, event.stanza; -- Filter out that claim to be from us -- cgit v1.2.3 From b199403ab512344f8f05ceef1a8bd20c399da5b8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 8 May 2020 22:39:01 +0200 Subject: mod_muc_mam: Fix missing advertising of XEP-0359, fixes #1547 --- plugins/mod_muc_mam.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 8216e2e6..88018f72 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -395,6 +395,7 @@ module:add_feature(xmlns_mam); module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var=xmlns_mam}):up(); + event.reply:tag("feature", {var=xmlns_st_id}):up(); end); -- Cleanup -- cgit v1.2.3 From 3e5bae8965ab0c707919cd1012881f0fa188a6d1 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 8 May 2020 23:47:48 +0200 Subject: mod_csi_simple: Consider XEP-0353: Jingle Message Initiation important Improves experience with VoIP calls initiated via JMI Closes #1548 --- plugins/mod_csi_simple.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/mod_csi_simple.lua b/plugins/mod_csi_simple.lua index da2dd953..7fb6b41f 100644 --- a/plugins/mod_csi_simple.lua +++ b/plugins/mod_csi_simple.lua @@ -79,6 +79,9 @@ module:hook("csi-is-stanza-important", function (event) if stanza:get_child("encryption", "urn:xmpp:eme:0") then return true; end + if stanza:get_child(nil, "urn:xmpp:jingle-message:0") then + return true; + end return false; end return true; -- cgit v1.2.3 From 3e4cbaf0f918f0c392518c055aef2593a6b9b1ed Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 11 May 2020 21:14:02 +0200 Subject: mod_muc_mam: Remove spoofed archive IDs before archiving, fix #1552 The stanza-id added during archiving looks exactly like what should be stripped, so the stripping must happen before archiving. Getting priorities right is hard! Also no test coverage yet. --- plugins/mod_muc_mam.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 88018f72..711bf163 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -324,7 +324,7 @@ module:hook("muc-broadcast-message", function (event) return tag; end); -end, 0); +end, 1); -- Handle messages local function save_to_history(self, stanza) -- cgit v1.2.3 From 984bf1c1bfc83537db694e0a340e5f44772ee6ba Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 15 May 2020 21:22:35 +0200 Subject: mod_storage_internal: Fix error in time limited queries on items without 'when' field, fixes #1557 --- plugins/mod_storage_internal.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/mod_storage_internal.lua b/plugins/mod_storage_internal.lua index 42b451bd..0becfc8f 100644 --- a/plugins/mod_storage_internal.lua +++ b/plugins/mod_storage_internal.lua @@ -104,12 +104,14 @@ function archive:find(username, query) end if query.start then items:filter(function (item) - return item.when >= query.start; + local when = item.when or datetime.parse(item.attr.stamp); + return when >= query.start; end); end if query["end"] then items:filter(function (item) - return item.when <= query["end"]; + local when = item.when or datetime.parse(item.attr.stamp); + return when <= query["end"]; end); end count = #items; -- cgit v1.2.3 From f72fa3b2abd2bf18540460b37bf9ca1c8a479b7f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 22 May 2020 20:59:01 +0200 Subject: util.sasl.scram: Apply saslprep before hashing password, fixes #1560 --- util/sasl/scram.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 043f328b..f64feb8b 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -106,6 +106,10 @@ local function getAuthenticationDatabaseSHA1(password, salt, iteration_count) if iteration_count < 4096 then log("warn", "Iteration count < 4096 which is the suggested minimum according to RFC 5802.") end + password = saslprep(password); + if not password then + return false, "password fails SASLprep"; + end local salted_password = Hi(password, salt, iteration_count); local stored_key = sha1(hmac_sha1(salted_password, "Client Key")) local server_key = hmac_sha1(salted_password, "Server Key"); -- cgit v1.2.3 From 7621990511255e6d9199af6451a4791abde3aea5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 22 May 2020 21:05:45 +0200 Subject: util.sasl.plain: Apply saslprep to stored password Fixes something like #1560 here too. The password sent by the user already had saslprep applied. --- util/sasl/plain.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/sasl/plain.lua b/util/sasl/plain.lua index 00c6bd20..43a66c5b 100644 --- a/util/sasl/plain.lua +++ b/util/sasl/plain.lua @@ -70,7 +70,7 @@ local function plain(self, message) if self.profile.plain then local correct_password; correct_password, state = self.profile.plain(self, authentication, self.realm); - correct = (correct_password == password); + correct = (saslprep(correct_password) == password); elseif self.profile.plain_test then correct, state = self.profile.plain_test(self, authentication, password, self.realm); end -- cgit v1.2.3 From 5a0f5d2de06dfec97d1f736286a8f6be1625dece Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 23 May 2020 14:17:04 +0200 Subject: mod_auth_internal_*: Apply saslprep to passwords Related to #1560 --- plugins/mod_auth_insecure.lua | 5 +++++ plugins/mod_auth_internal_hashed.lua | 7 ++++++- plugins/mod_auth_internal_plain.lua | 15 ++++++++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/plugins/mod_auth_insecure.lua b/plugins/mod_auth_insecure.lua index 9e23c29f..dc5ee616 100644 --- a/plugins/mod_auth_insecure.lua +++ b/plugins/mod_auth_insecure.lua @@ -9,6 +9,7 @@ local datamanager = require "util.datamanager"; local new_sasl = require "util.sasl".new; +local saslprep = require "util.encodings".stringprep.saslprep; local host = module.host; local provider = { name = "insecure" }; @@ -21,6 +22,10 @@ end function provider.set_password(username, password) local account = datamanager.load(username, host, "accounts"); + password = saslprep(password); + if not password then + return nil, "Password fails SASLprep."; + end if account then account.password = password; return datamanager.store(username, host, "accounts", account); diff --git a/plugins/mod_auth_internal_hashed.lua b/plugins/mod_auth_internal_hashed.lua index 083f648b..15058098 100644 --- a/plugins/mod_auth_internal_hashed.lua +++ b/plugins/mod_auth_internal_hashed.lua @@ -15,6 +15,7 @@ local generate_uuid = require "util.uuid".generate; local new_sasl = require "util.sasl".new; local hex = require"util.hex"; local to_hex, from_hex = hex.to, hex.from; +local saslprep = require "util.encodings".stringprep.saslprep; local log = module._log; local host = module.host; @@ -32,9 +33,13 @@ local provider = {}; function provider.test_password(username, password) log("debug", "test password for user '%s'", username); local credentials = accounts:get(username) or {}; + password = saslprep(password); + if not password then + return nil, "Password fails SASLprep."; + end if credentials.password ~= nil and string.len(credentials.password) ~= 0 then - if credentials.password ~= password then + if saslprep(credentials.password) ~= password then return nil, "Auth failed. Provided password is incorrect."; end diff --git a/plugins/mod_auth_internal_plain.lua b/plugins/mod_auth_internal_plain.lua index 276efb64..56ef52d5 100644 --- a/plugins/mod_auth_internal_plain.lua +++ b/plugins/mod_auth_internal_plain.lua @@ -8,6 +8,7 @@ local usermanager = require "core.usermanager"; local new_sasl = require "util.sasl".new; +local saslprep = require "util.encodings".stringprep.saslprep; local log = module._log; local host = module.host; @@ -20,8 +21,12 @@ local provider = {}; function provider.test_password(username, password) log("debug", "test password for user '%s'", username); local credentials = accounts:get(username) or {}; + password = saslprep(password); + if not password then + return nil, "Password fails SASLprep."; + end - if password == credentials.password then + if password == saslprep(credentials.password) then return true; else return nil, "Auth failed. Invalid username or password."; @@ -35,6 +40,10 @@ end function provider.set_password(username, password) log("debug", "set_password for username '%s'", username); + password = saslprep(password); + if not password then + return nil, "Password fails SASLprep."; + end local account = accounts:get(username); if account then account.password = password; @@ -57,6 +66,10 @@ function provider.users() end function provider.create_user(username, password) + password = saslprep(password); + if not password then + return nil, "Password fails SASLprep."; + end return accounts:set(username, {password = password}); end -- cgit v1.2.3 From 396ae378944abc3981024617ae87605974364335 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 21 Jun 2020 19:20:54 +0200 Subject: mod_muc_mam: Don't strip MUC tags, fix #1567 --- plugins/mod_muc_mam.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 711bf163..f0417889 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -318,9 +318,6 @@ module:hook("muc-broadcast-message", function (event) and jid_prep(tag.attr.by) == room.jid then return nil; end - if tag.name == "x" and tag.attr.xmlns == xmlns_muc_user then - return nil; - end return tag; end); -- cgit v1.2.3 From 7448c5c1b181055cabbe707c2deb9e4b575df507 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 22 Jun 2020 14:56:44 +0200 Subject: util.startup: Remove duplicated initialization of logging (fix #1527) --- util/startup.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/util/startup.lua b/util/startup.lua index 24ed6026..2a7e8a36 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -597,7 +597,6 @@ function startup.prosody() startup.sandbox_require(); startup.set_function_metatable(); startup.check_dependencies(); - startup.init_logging(); startup.load_libraries(); startup.setup_plugindir(); startup.setup_datadir(); -- cgit v1.2.3 From d12c7e9416b242120df35419220a332196cd8413 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 23 Jun 2020 15:39:31 +0200 Subject: net.http.server: Strip port from Host header in IPv6 friendly way (fix #1302) E.g. given `[::1]:5280` it would previously result in only `[` instead of the correct `[::1]` --- net/http/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index 9b63d516..d0bd3294 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -207,7 +207,7 @@ function handle_request(conn, request, finish_cb) }; conn._http_open_response = response; - local host = (request.headers.host or ""):match("[^:]+"); + local host = (request.headers.host or ""):gsub(":%d+$",""); -- Some sanity checking local err_code, err; -- cgit v1.2.3 From ba013abb83c578589b4ed413b5a330d9e835d764 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 23 Jun 2020 15:43:57 +0200 Subject: net.http.server: Fix reporting of missing Host header The "Missing or invalid 'Host' header" case was dead code previously because `host` was always at least an empty string. --- net/http/server.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index d0bd3294..18704962 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -207,7 +207,8 @@ function handle_request(conn, request, finish_cb) }; conn._http_open_response = response; - local host = (request.headers.host or ""):gsub(":%d+$",""); + local host = request.headers.host; + if host then host = host:gsub(":%d+$",""); end -- Some sanity checking local err_code, err; -- cgit v1.2.3 From cc5eeb60cdba4f8aea1d45b7c8a5c2a532462d2e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 7 Jul 2020 13:52:25 +0100 Subject: net.http: Fix traceback on invalid URL passed to request() --- net/http.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/http.lua b/net/http.lua index fe5250ac..0adac26c 100644 --- a/net/http.lua +++ b/net/http.lua @@ -183,14 +183,15 @@ end local function request(self, u, ex, callback) local req = url.parse(u); - req.url = u; - req.http = self; if not (req and req.host) then callback("invalid-url", 0, req); return nil, "invalid-url"; end + req.url = u; + req.http = self; + if not req.path then req.path = "/"; end -- cgit v1.2.3 From e3a2cc2d407e3b1736843dd90f898caf53a8cd3a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 10 Jul 2020 13:00:02 +0100 Subject: net.resolvers.basic: Default conn_type to 'tcp' consistently if unspecified (thanks marc0s) Fixes a traceback when passed an IP address with no conn_type. --- net/resolvers/basic.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/resolvers/basic.lua b/net/resolvers/basic.lua index 08c71ef5..867ccf60 100644 --- a/net/resolvers/basic.lua +++ b/net/resolvers/basic.lua @@ -58,6 +58,7 @@ end local function new(hostname, port, conn_type, extra) local ascii_host = idna_to_ascii(hostname); local targets = nil; + conn_type = conn_type or "tcp"; local is_ip = inet_pton(hostname); if not is_ip and hostname:sub(1,1) == '[' then @@ -75,7 +76,7 @@ local function new(hostname, port, conn_type, extra) return setmetatable({ hostname = ascii_host; port = port; - conn_type = conn_type or "tcp"; + conn_type = conn_type; extra = extra; targets = targets; }, resolver_mt); -- cgit v1.2.3 From 1ec3ab3c2fc96368675e3b679cf50c3396ef8369 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 8 Aug 2020 13:11:11 +0100 Subject: net.http: Re-expose destroy_request() function This was accidentally turned private in 647adfd8f738 as part of refactoring for Lua 5.2+. --- net/http.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/net/http.lua b/net/http.lua index 0adac26c..14107bf4 100644 --- a/net/http.lua +++ b/net/http.lua @@ -302,4 +302,5 @@ return { urldecode = util_http.urldecode; formencode = util_http.formencode; formdecode = util_http.formdecode; + destroy_request = destroy_request; }; -- cgit v1.2.3 From 09fd5f5c24ce919563cdadf0919901664cc3de30 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 8 Aug 2020 13:13:50 +0100 Subject: net.http: Add request:cancel() method This is a new API that should be used in preference to http.destroy_request() when possible, as it ensures the callback is always called (with an error of course). APIs that have edge-cases where they don't call callbacks have, from experience, shown to be difficult to work with and often lead to unintentional leaks when the callback was expected to free up certain resources. --- net/http.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/http.lua b/net/http.lua index 14107bf4..ae9d2974 100644 --- a/net/http.lua +++ b/net/http.lua @@ -56,6 +56,16 @@ local function destroy_request(request) end end +local function cancel_request(request, reason) + if request.callback then + request.callback(reason or "cancelled", 0, request); + request.callback = nil; + end + if request.conn then + destroy_request(request); + end +end + local function request_reader(request, data, err) if not request.parser then local function error_cb(reason) @@ -105,6 +115,7 @@ function listener.onconnect(conn) end req.reader = request_reader; req.state = "status"; + req.cancel = cancel_request; requests[req.conn] = req; -- cgit v1.2.3 From d0178fb089d0c757051959114b10e67820dfd981 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 1 Aug 2020 12:58:37 +0200 Subject: mod_s2s: Escape invalid XML in loggin (same way as mod_c2s) fix #1574 --- plugins/mod_s2s/mod_s2s.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/mod_s2s/mod_s2s.lua b/plugins/mod_s2s/mod_s2s.lua index aae37b7f..3f56b953 100644 --- a/plugins/mod_s2s/mod_s2s.lua +++ b/plugins/mod_s2s/mod_s2s.lua @@ -595,8 +595,7 @@ local function initialize_session(session) if data then local ok, err = stream:feed(data); if ok then return; end - log("warn", "Received invalid XML: %s", data); - log("warn", "Problem was: %s", err); + log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_")); session:close("not-well-formed"); end end -- cgit v1.2.3 -- cgit v1.2.3 From ce940fed493f2c138fa43e220c0ebef3c846f09d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 17 Aug 2020 23:01:14 +0200 Subject: net.server: Backport client parts of SNI support from trunk (#409) Partial backports of the following commits from trunk: 6c804b6b2ca2 net.http: Pass server name along for SNI (fixes #1408) 75d2874502c3 net.server_select: SNI support (#409) 9a905888b96c net.server_event: Add SNI support (#409) adc0672b700e net.server_epoll: Add support for SNI (#409) d4390c427a66 net.server: Handle server name (SNI) as extra argument --- net/http.lua | 2 +- net/server_epoll.lua | 20 +++++++++++++++----- net/server_event.lua | 17 ++++++++++++----- net/server_select.lua | 19 ++++++++++++++----- 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/net/http.lua b/net/http.lua index ae9d2974..0768cdab 100644 --- a/net/http.lua +++ b/net/http.lua @@ -272,7 +272,7 @@ local function request(self, u, ex, callback) 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 }); diff --git a/net/server_epoll.lua b/net/server_epoll.lua index 2182d56a..953bbb11 100644 --- a/net/server_epoll.lua +++ b/net/server_epoll.lua @@ -483,6 +483,9 @@ function interface:tlshandskake() 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 @@ function interface:tlshandskake() 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 @@ local function wrapsocket(client, server, read_size, listeners, tls_ctx) -- luas 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 @@ local function addserver(addr, port, listeners, read_size, tls_ctx) 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 @@ local function wrapclient(conn, addr, port, listeners, read_size, tls_ctx) 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 @@ local function addclient(addr, port, listeners, read_size, tls_ctx, typ) 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 --git a/net/server_event.lua b/net/server_event.lua index 11bd6a29..746526ce 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -164,6 +164,11 @@ function interface_mt:_start_ssl(call_onconnect) -- old socket will be destroyed 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 -- 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 @@ local function handleclient( client, ip, port, server, pattern, listener, sslctx _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 @@ local function addserver( addr, port, listener, pattern, sslctx, startssl ) -- 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 function addclient( addr, serverport, listener, pattern, sslctx, typ ) 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 --git a/net/server_select.lua b/net/server_select.lua index 1a40a6d3..deb8fe48 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -264,7 +264,7 @@ wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx ) -- t 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 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport 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 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport 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 @@ end --// 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 @@ local wrapclient = function( socket, ip, serverport, listeners, pattern, sslctx 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 @@ local addclient = function( address, port, listeners, pattern, sslctx, typ ) 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 -- cgit v1.2.3 From 0b3dfb6e44835e521e88359fb4b0ccc3d52f57c4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 15 Sep 2020 09:08:21 +0100 Subject: net.http: Add feature discovery (currently just contains SNI) --- net/http.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/http.lua b/net/http.lua index 0768cdab..7335d210 100644 --- a/net/http.lua +++ b/net/http.lua @@ -314,4 +314,7 @@ return { formencode = util_http.formencode; formdecode = util_http.formdecode; destroy_request = destroy_request; + features = { + sni = true; + }; }; -- cgit v1.2.3 From 5f8f75ddab1610b193ced17dec46164b6db72db8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 15 Jun 2020 14:16:10 +0100 Subject: util.gc: New module for configuring the Lua garbage collector --- util/gc.lua | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 util/gc.lua diff --git a/util/gc.lua b/util/gc.lua new file mode 100644 index 00000000..e02e85c4 --- /dev/null +++ b/util/gc.lua @@ -0,0 +1,50 @@ +local array = require "util.array"; +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; +}; -- cgit v1.2.3 From f46f4b97ebf16c4816d5e8847baeb062c0212bd9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 15 Jun 2020 14:23:47 +0100 Subject: util.gc: Linter fixes [luacheck] --- util/gc.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/util/gc.lua b/util/gc.lua index e02e85c4..b400af6b 100644 --- a/util/gc.lua +++ b/util/gc.lua @@ -1,4 +1,3 @@ -local array = require "util.array"; local set = require "util.set"; local known_options = { @@ -41,7 +40,7 @@ local function configure(user, defaults) user.minor_threshold or defaults.minor_threshold, user.major_threshold or defaults.major_threshold ); - end + end return true; end -- cgit v1.2.3 From e83907f4990b671d8953f64f1b2663d0df0226d4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 15 Jun 2020 14:16:34 +0100 Subject: util.startup: Configure the GC on startup, using the config or built-in defaults --- util/startup.lua | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/util/startup.lua b/util/startup.lua index 2a7e8a36..c1f4ec8b 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -12,6 +12,8 @@ local dependencies = require "util.dependencies"; 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 @@ function startup.check_unwriteable() 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 @@ function startup.prosodyctl() 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 @@ function startup.prosody() startup.init_global_state(); startup.read_config(); startup.init_logging(); + startup.init_gc(); startup.sanity_check(); startup.sandbox_require(); startup.set_function_metatable(); -- cgit v1.2.3 From 6774f93b5da1642c1baef1387880da484428981e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 26 Jun 2020 16:41:31 +0100 Subject: util.dbuffer: dynamic string buffer Similar to util.ringbuffer (and shares almost identical API). Differences: - size limit is optional and dynamic - does not allocate a fixed buffer of max_size bytes - focus on simply storing references to existing string objects where possible, avoiding unnecessary allocations - references are still stored in a ring buffer to enable use as a fast FIFO Optional second parameter to new() provides the number of ring buffer segments. On Lua 5.2 on my laptop, a segment is ~19 bytes. If the ring buffer fills up, the next write will compact all strings into a single item. --- util/queue.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/util/queue.lua b/util/queue.lua index 728e905f..c8e71514 100644 --- a/util/queue.lua +++ b/util/queue.lua @@ -51,6 +51,13 @@ local function new(size, allow_wrapping) 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) -- cgit v1.2.3 From 0156ad50d3f7b8fcfc12f0ebf9d2c2fee4df36ee Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 24 Aug 2020 16:18:13 +0100 Subject: util.dbuffer: Fix :sub() not working with partially-consumed chunks (thanks Zash for test case) This also appears to fix some bugs with chunk-encoded streams in net.http.parser. --- util/dbuffer.lua | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 util/dbuffer.lua diff --git a/util/dbuffer.lua b/util/dbuffer.lua new file mode 100644 index 00000000..a50f3a64 --- /dev/null +++ b/util/dbuffer.lua @@ -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; +}; -- cgit v1.2.3 From ee0fe27609bccc9752cdbff100dace64e2c4d55b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 24 Aug 2020 17:28:48 +0200 Subject: util.dbuffer: Simplify test case An earlier theory involved the bug being related to collapsing multiple items, so it exercised that too. Also correct the comment, it referred to the space in "hello world" in an earlier version before the test string was changed to "foobar", which was what was tested in a REPL --- spec/util_dbuffer_spec.lua | 130 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 spec/util_dbuffer_spec.lua diff --git a/spec/util_dbuffer_spec.lua b/spec/util_dbuffer_spec.lua new file mode 100644 index 00000000..af404042 --- /dev/null +++ b/spec/util_dbuffer_spec.lua @@ -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); -- cgit v1.2.3 From f9550cb3416b5c579b7efabe85495951464868c8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 17 Sep 2020 13:00:19 +0100 Subject: net.websocket.frames: Allow all methods to work on non-string objects Instead of using the string library, use methods from the passed object, which are assumed to be equivalent. This provides compatibility with objects from util.ringbuffer and util.dbuffer, for example. --- net/websocket/frames.lua | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/net/websocket/frames.lua b/net/websocket/frames.lua index ba25d261..e1b5527a 100644 --- a/net/websocket/frames.lua +++ b/net/websocket/frames.lua @@ -16,13 +16,12 @@ local bor = bit.bor; 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 +local s_pack = string.pack; +local s_unpack = string.unpack; if not s_pack and softreq"struct" then s_pack = softreq"struct".pack; @@ -30,12 +29,12 @@ if not s_pack and softreq"struct" then 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 @@ end 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 @@ end 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 @@ local function parse_frame_header(frame) 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 @@ local function apply_mask(str, key, from, to) 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 @@ -189,7 +194,7 @@ local function parse_close(data) 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 -- cgit v1.2.3 From d8be48981f7e64f984c05f88ecf381da831f7232 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 17 Sep 2020 13:04:46 +0100 Subject: mod_websocket: Switch partial frame buffering to util.dbuffer This improves performance and enforces stanza size limits earlier in the pipeline. --- net/websocket/frames.lua | 4 ++-- plugins/mod_websocket.lua | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/net/websocket/frames.lua b/net/websocket/frames.lua index e1b5527a..65f9122c 100644 --- a/net/websocket/frames.lua +++ b/net/websocket/frames.lua @@ -20,8 +20,8 @@ local unpack = table.unpack or unpack; -- luacheck: ignore 113 local t_concat = table.concat; local s_char= string.char; -local s_pack = string.pack; -local s_unpack = string.unpack; +local s_pack = string.pack; -- luacheck: ignore 143 +local s_unpack = string.unpack; -- luacheck: ignore 143 if not s_pack and softreq"struct" then s_pack = softreq"struct".pack; diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index 177259e6..ad94486b 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -18,6 +18,7 @@ local contains_token = require "util.http".contains_token; 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,8 @@ local parse_close = websocket_frames.parse_close; local t_concat = table.concat; +local stanza_size_limit = module:get_option_number("c2s_stanza_size_limit", 10 * 1024 * 1024); +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", {}); @@ -269,14 +272,16 @@ function handle_request(event) session.open_stream = session_open_stream; session.close = session_close; - local frameBuffer = ""; + -- max frame header is 22 bytes + local frameBuffer = dbuffer.new(stanza_size_limit + 22, frame_fragment_limit); add_filter(session, "bytes/in", function(data) + frameBuffer:write(data); + local cache = {}; - frameBuffer = frameBuffer .. data; local frame, length = parse_frame(frameBuffer); while frame do - frameBuffer = frameBuffer:sub(length + 1); + frameBuffer:discard(length); local result = handle_frame(frame); if not result then return; end cache[#cache+1] = filter_open_close(result); -- cgit v1.2.3 From 7f839ba44930e5384a2c170e136ef837c7b54cd2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 17 Sep 2020 16:41:48 +0100 Subject: mod_websocket: handle full frame buffer and raise stream error --- plugins/mod_websocket.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index ad94486b..d3b035db 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -275,7 +275,11 @@ function handle_request(event) -- max frame header is 22 bytes local frameBuffer = dbuffer.new(stanza_size_limit + 22, frame_fragment_limit); add_filter(session, "bytes/in", function(data) - frameBuffer:write(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 = {}; local frame, length = parse_frame(frameBuffer); -- cgit v1.2.3 From 2eee29ff1eec57e9204f2487b0349abb80e9c9f0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 17 Sep 2020 16:42:14 +0100 Subject: mod_websocket: Add separate limit for frame buffer size --- plugins/mod_websocket.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index d3b035db..53a1d452 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -29,6 +29,7 @@ local parse_close = websocket_frames.parse_close; 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"); @@ -272,8 +273,7 @@ function handle_request(event) session.open_stream = session_open_stream; session.close = session_close; - -- max frame header is 22 bytes - local frameBuffer = dbuffer.new(stanza_size_limit + 22, frame_fragment_limit); + 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"); -- cgit v1.2.3 From 68898a08805177d6e2ad52c88580893eed506d66 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 17 Sep 2020 16:42:36 +0100 Subject: mod_websocket: Enforce stanza size limit and close stream --- plugins/mod_websocket.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index 53a1d452..0bd001f4 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -285,6 +285,10 @@ function handle_request(event) local frame, length = parse_frame(frameBuffer); while frame do + if length > stanza_size_limit then + session:close({ condition = "policy-violation", text = "stanza too large" }); + return; + end frameBuffer:discard(length); local result = handle_frame(frame); if not result then return; end -- cgit v1.2.3 From fb2a77467f01351f3b88d45b436fd7420eba90dc Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 28 Sep 2020 16:36:12 +0100 Subject: mod_websocket: Continue to process data already in the buffer after an error occurs Previously any error, or even a normal websocket close frame, would return early, leaving potentially entire frames in the buffer unprocessed and then discarded. This change stops processing new data, but returns an existing processed data up to the point of the error/close. --- plugins/mod_websocket.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index 0bd001f4..ecc12a45 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -291,7 +291,7 @@ function handle_request(event) end frameBuffer:discard(length); local result = handle_frame(frame); - if not result then return; end + if not result then break; end cache[#cache+1] = filter_open_close(result); frame, length = parse_frame(frameBuffer); end -- cgit v1.2.3 From 3cd5ae91199caf59972c2d6d953fa3619da8a552 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 29 Sep 2020 13:58:32 +0100 Subject: net.websocket.frames: Additionally return partial frame if there is one --- net/websocket/frames.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/websocket/frames.lua b/net/websocket/frames.lua index 65f9122c..a0c0d4cd 100644 --- a/net/websocket/frames.lua +++ b/net/websocket/frames.lua @@ -141,7 +141,7 @@ end 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 -- cgit v1.2.3 From 2546c272bc9f683885d2b4943e70e21696a4c762 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 29 Sep 2020 15:18:32 +0100 Subject: mod_websocket: Refactor frame validity checking, also check partially-received frames against constraints --- plugins/mod_websocket.lua | 172 +++++++++++++++++++++++++--------------------- 1 file changed, 95 insertions(+), 77 deletions(-) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index ecc12a45..a613672b 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -142,6 +142,65 @@ local function filter_open_close(data) 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; @@ -172,90 +231,40 @@ function handle_request(event) 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); @@ -282,19 +291,28 @@ function handle_request(event) end local cache = {}; - local frame, length = parse_frame(frameBuffer); + local frame, length, partial = parse_frame(frameBuffer); while frame do - if length > stanza_size_limit then - session:close({ condition = "policy-violation", text = "stanza too large" }); - return; - end frameBuffer:discard(length); - local result = handle_frame(frame); - if not result then break; end + 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); -- cgit v1.2.3