aboutsummaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/adhoc/adhoc.lib.lua2
-rw-r--r--plugins/adhoc/mod_adhoc.lua4
-rw-r--r--plugins/mod_admin_shell.lua69
-rw-r--r--plugins/mod_debug_stanzas/watcher.lib.lua220
-rw-r--r--plugins/mod_s2s.lua42
-rw-r--r--plugins/mod_smacks.lua37
6 files changed, 330 insertions, 44 deletions
diff --git a/plugins/adhoc/adhoc.lib.lua b/plugins/adhoc/adhoc.lib.lua
index 4cf6911d..eb91f252 100644
--- a/plugins/adhoc/adhoc.lib.lua
+++ b/plugins/adhoc/adhoc.lib.lua
@@ -34,6 +34,8 @@ function _M.handle_cmd(command, origin, stanza)
local cmdtag = stanza.tags[1]
local sessionid = cmdtag.attr.sessionid or uuid.generate();
local dataIn = {
+ origin = origin;
+ stanza = stanza;
to = stanza.attr.to;
from = stanza.attr.from;
action = cmdtag.attr.action or "execute";
diff --git a/plugins/adhoc/mod_adhoc.lua b/plugins/adhoc/mod_adhoc.lua
index 09a72075..9d6ff77a 100644
--- a/plugins/adhoc/mod_adhoc.lua
+++ b/plugins/adhoc/mod_adhoc.lua
@@ -79,12 +79,12 @@ module:hook("iq-set/host/"..xmlns_cmd..":command", function (event)
or (command.permission == "global_admin" and not global_admin)
or (command.permission == "local_user" and hostname ~= module.host) then
origin.send(st.error_reply(stanza, "auth", "forbidden", "You don't have permission to execute this command"):up()
- :add_child(commands[node]:cmdtag("canceled")
+ :add_child(command:cmdtag("canceled")
:tag("note", {type="error"}):text("You don't have permission to execute this command")));
return true
end
-- User has permission now execute the command
- adhoc_handle_cmd(commands[node], origin, stanza);
+ adhoc_handle_cmd(command, origin, stanza);
return true;
end
end, 500);
diff --git a/plugins/mod_admin_shell.lua b/plugins/mod_admin_shell.lua
index 35124e79..ae7e3c7c 100644
--- a/plugins/mod_admin_shell.lua
+++ b/plugins/mod_admin_shell.lua
@@ -36,6 +36,7 @@ local serialization = require "util.serialization";
local serialize_config = serialization.new ({ fatal = false, unquoted = true});
local time = require "util.time";
local promise = require "util.promise";
+local logger = require "util.logger";
local t_insert = table.insert;
local t_concat = table.concat;
@@ -83,8 +84,8 @@ function runner_callbacks:error(err)
self.data.print("Error: "..tostring(err));
end
-local function send_repl_output(session, line)
- return session.send(st.stanza("repl-output"):text(tostring(line)));
+local function send_repl_output(session, line, attr)
+ return session.send(st.stanza("repl-output", attr):text(tostring(line)));
end
function console:new_session(admin_session)
@@ -99,8 +100,14 @@ function console:new_session(admin_session)
end
return send_repl_output(admin_session, table.concat(t, "\t"));
end;
+ write = function (t)
+ return send_repl_output(admin_session, t, { eol = "0" });
+ end;
serialize = tostring;
disconnect = function () admin_session:close(); end;
+ is_connected = function ()
+ return not not admin_session.conn;
+ end
};
session.env = setmetatable({}, default_env_mt);
@@ -1583,6 +1590,60 @@ function def_env.http:list(hosts)
return true;
end
+def_env.watch = {};
+
+function def_env.watch:log()
+ local writing = false;
+ local sink = logger.add_simple_sink(function (source, level, message)
+ if writing then return; end
+ writing = true;
+ self.session.print(source, level, message);
+ writing = false;
+ end);
+
+ while self.session.is_connected() do
+ async.sleep(3);
+ end
+ if not logger.remove_sink(sink) then
+ module:log("warn", "Unable to remove watch:log() sink");
+ end
+end
+
+local stanza_watchers = module:require("mod_debug_stanzas/watcher");
+function def_env.watch:stanzas(target_spec, filter_spec)
+ local function handler(event_type, stanza, session)
+ if stanza then
+ if event_type == "sent" then
+ self.session.print(("\n<!-- sent to %s -->"):format(session.id));
+ elseif event_type == "received" then
+ self.session.print(("\n<!-- received from %s -->"):format(session.id));
+ else
+ self.session.print(("\n<!-- %s (%s) -->"):format(event_type, session.id));
+ end
+ self.session.print(stanza);
+ elseif session then
+ self.session.print("\n<!-- session "..session.id.." "..event_type.." -->");
+ elseif event_type then
+ self.session.print("\n<!-- "..event_type.." -->");
+ end
+ end
+
+ stanza_watchers.add({
+ target_spec = {
+ jid = target_spec;
+ };
+ filter_spec = filter_spec and {
+ with_jid = filter_spec;
+ };
+ }, handler);
+
+ while self.session.is_connected() do
+ async.sleep(3);
+ end
+
+ stanza_watchers.remove(handler);
+end
+
def_env.debug = {};
function def_env.debug:logevents(host)
@@ -1926,6 +1987,10 @@ function def_env.stats:show(name_filter)
end
+function module.unload()
+ stanza_watchers.cleanup();
+end
+
-------------
diff --git a/plugins/mod_debug_stanzas/watcher.lib.lua b/plugins/mod_debug_stanzas/watcher.lib.lua
new file mode 100644
index 00000000..e21fc946
--- /dev/null
+++ b/plugins/mod_debug_stanzas/watcher.lib.lua
@@ -0,0 +1,220 @@
+local filters = require "util.filters";
+local jid = require "util.jid";
+local set = require "util.set";
+
+local client_watchers = {};
+
+-- active_filters[session] = {
+-- filter_func = filter_func;
+-- downstream = { cb1, cb2, ... };
+-- }
+local active_filters = {};
+
+local function subscribe_session_stanzas(session, handler, reason)
+ if active_filters[session] then
+ table.insert(active_filters[session].downstream, handler);
+ if reason then
+ handler(reason, nil, session);
+ end
+ return;
+ end
+ local downstream = { handler };
+ active_filters[session] = {
+ filter_in = function (stanza)
+ module:log("debug", "NOTIFY WATCHER %d", #downstream);
+ for i = 1, #downstream do
+ downstream[i]("received", stanza, session);
+ end
+ return stanza;
+ end;
+ filter_out = function (stanza)
+ module:log("debug", "NOTIFY WATCHER %d", #downstream);
+ for i = 1, #downstream do
+ downstream[i]("sent", stanza, session);
+ end
+ return stanza;
+ end;
+ downstream = downstream;
+ };
+ filters.add_filter(session, "stanzas/in", active_filters[session].filter_in);
+ filters.add_filter(session, "stanzas/out", active_filters[session].filter_out);
+ if reason then
+ handler(reason, nil, session);
+ end
+end
+
+local function unsubscribe_session_stanzas(session, handler, reason)
+ local active_filter = active_filters[session];
+ if not active_filter then
+ return;
+ end
+ for i = #active_filter.downstream, 1, -1 do
+ if active_filter.downstream[i] == handler then
+ table.remove(active_filter.downstream, i);
+ if reason then
+ handler(reason, nil, session);
+ end
+ end
+ end
+ if #active_filter.downstream == 0 then
+ filters.remove_filter(session, "stanzas/in", active_filter.filter_in);
+ filters.remove_filter(session, "stanzas/out", active_filter.filter_out);
+ end
+ active_filters[session] = nil;
+end
+
+local function unsubscribe_all_from_session(session, reason)
+ local active_filter = active_filters[session];
+ if not active_filter then
+ return;
+ end
+ for i = #active_filter.downstream, 1, -1 do
+ local handler = table.remove(active_filter.downstream, i);
+ if reason then
+ handler(reason, nil, session);
+ end
+ end
+ filters.remove_filter(session, "stanzas/in", active_filter.filter_in);
+ filters.remove_filter(session, "stanzas/out", active_filter.filter_out);
+ active_filters[session] = nil;
+end
+
+local function unsubscribe_handler_from_all(handler, reason)
+ for session in pairs(active_filters) do
+ unsubscribe_session_stanzas(session, handler, reason);
+ end
+end
+
+local s2s_watchers = {};
+
+module:hook("s2sin-established", function (event)
+ for _, watcher in ipairs(s2s_watchers) do
+ if watcher.target_spec == event.session.from_host then
+ subscribe_session_stanzas(event.session, watcher.handler, "opened");
+ end
+ end
+end);
+
+module:hook("s2sout-established", function (event)
+ for _, watcher in ipairs(s2s_watchers) do
+ if watcher.target_spec == event.session.to_host then
+ subscribe_session_stanzas(event.session, watcher.handler, "opened");
+ end
+ end
+end);
+
+module:hook("s2s-closed", function (event)
+ unsubscribe_all_from_session(event.session, "closed");
+end);
+
+local watched_hosts = set.new();
+
+local handler_map = setmetatable({}, { __mode = "kv" });
+
+local function add_stanza_watcher(spec, orig_handler)
+ local function filtering_handler(event_type, stanza, session)
+ if stanza and spec.filter_spec then
+ if spec.filter_spec.with_jid then
+ if event_type == "sent" and (not stanza.attr.from or not jid.compare(stanza.attr.from, spec.filter_spec.with_jid)) then
+ return;
+ elseif event_type == "received" and (not stanza.attr.to or not jid.compare(stanza.attr.to, spec.filter_spec.with_jid)) then
+ return;
+ end
+ end
+ end
+ return orig_handler(event_type, stanza, session);
+ end
+ handler_map[orig_handler] = filtering_handler;
+ if spec.target_spec.jid then
+ local target_is_remote_host = not jid.node(spec.target_spec.jid) and not prosody.hosts[spec.target_spec.jid];
+
+ if target_is_remote_host then
+ -- Watch s2s sessions
+ table.insert(s2s_watchers, {
+ target_spec = spec.target_spec.jid;
+ handler = filtering_handler;
+ orig_handler = orig_handler;
+ });
+
+ -- Scan existing s2sin for matches
+ for session in pairs(prosody.incoming_s2s) do
+ if spec.target_spec.jid == session.from_host then
+ subscribe_session_stanzas(session, filtering_handler, "attached");
+ end
+ end
+ -- Scan existing s2sout for matches
+ for local_host, local_session in pairs(prosody.hosts) do --luacheck: ignore 213/local_host
+ for remote_host, remote_session in pairs(local_session.s2sout) do
+ if spec.target_spec.jid == remote_host then
+ subscribe_session_stanzas(remote_session, filtering_handler, "attached");
+ end
+ end
+ end
+ else
+ table.insert(client_watchers, {
+ target_spec = spec.target_spec.jid;
+ handler = filtering_handler;
+ orig_handler = orig_handler;
+ });
+ local host = jid.host(spec.target_spec.jid);
+ if not watched_hosts:contains(host) and prosody.hosts[host] then
+ module:context(host):hook("resource-bind", function (event)
+ for _, watcher in ipairs(client_watchers) do
+ module:log("debug", "NEW CLIENT: %s vs %s", event.session.full_jid, watcher.target_spec);
+ if jid.compare(event.session.full_jid, watcher.target_spec) then
+ module:log("debug", "MATCH");
+ subscribe_session_stanzas(event.session, watcher.handler, "opened");
+ else
+ module:log("debug", "NO MATCH");
+ end
+ end
+ end);
+
+ module:context(host):hook("resource-unbind", function (event)
+ unsubscribe_all_from_session(event.session, "closed");
+ end);
+
+ watched_hosts:add(host);
+ end
+ for full_jid, session in pairs(prosody.full_sessions) do
+ if jid.compare(full_jid, spec.target_spec.jid) then
+ subscribe_session_stanzas(session, filtering_handler, "attached");
+ end
+ end
+ end
+ else
+ error("No recognized target selector");
+ end
+end
+
+local function remove_stanza_watcher(orig_handler)
+ local handler = handler_map[orig_handler];
+ unsubscribe_handler_from_all(handler, "detached");
+ handler_map[orig_handler] = nil;
+
+ for i = #client_watchers, 1, -1 do
+ if client_watchers[i].orig_handler == orig_handler then
+ table.remove(client_watchers, i);
+ end
+ end
+
+ for i = #s2s_watchers, 1, -1 do
+ if s2s_watchers[i].orig_handler == orig_handler then
+ table.remove(s2s_watchers, i);
+ end
+ end
+end
+
+local function cleanup(reason)
+ client_watchers = {};
+ s2s_watchers = {};
+ for session in pairs(active_filters) do
+ unsubscribe_all_from_session(session, reason or "cancelled");
+ end
+end
+
+return {
+ add = add_stanza_watcher;
+ remove = remove_stanza_watcher;
+ cleanup = cleanup;
+};
diff --git a/plugins/mod_s2s.lua b/plugins/mod_s2s.lua
index e810c6cd..2f3815c4 100644
--- a/plugins/mod_s2s.lua
+++ b/plugins/mod_s2s.lua
@@ -146,16 +146,14 @@ local function bounce_sendq(session, reason)
elseif type(reason) == "string" then
reason_text = reason;
end
- for i, data in ipairs(sendq) do
- local reply = data[2];
- if reply and not(reply.attr.xmlns) and bouncy_stanzas[reply.name] then
- reply.attr.type = "error";
- reply:tag("error", {type = error_type, by = session.from_host})
- :tag(condition, {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):up();
- if reason_text then
- reply:tag("text", {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"})
- :text("Server-to-server connection failed: "..reason_text):up();
- end
+ for i, stanza in ipairs(sendq) do
+ if not stanza.attr.xmlns and bouncy_stanzas[stanza.name] then
+ local reply = st.error_reply(
+ stanza,
+ error_type,
+ condition,
+ reason_text and ("Server-to-server connection failed: "..reason_text) or nil
+ );
core_process_stanza(dummy, reply);
end
sendq[i] = nil;
@@ -182,15 +180,11 @@ function route_to_existing_session(event)
(host.log or log)("debug", "trying to send over unauthed s2sout to "..to_host);
-- Queue stanza until we are able to send it
- local queued_item = {
- tostring(stanza),
- stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza);
- };
if host.sendq then
- t_insert(host.sendq, queued_item);
+ t_insert(host.sendq, st.clone(stanza));
else
-- luacheck: ignore 122
- host.sendq = { queued_item };
+ host.sendq = { st.clone(stanza) };
end
host.log("debug", "stanza [%s] queued ", stanza.name);
return true;
@@ -215,7 +209,7 @@ function route_to_new_session(event)
-- Store in buffer
host_session.bounce_sendq = bounce_sendq;
- host_session.sendq = { {tostring(stanza), stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)} };
+ host_session.sendq = { st.clone(stanza) };
log("debug", "stanza [%s] queued until connection complete", stanza.name);
-- FIXME Cleaner solution to passing extra data from resolvers to net.server
-- This mt-clone allows resolvers to add extra data, currently used for DANE TLSA records
@@ -324,8 +318,8 @@ function mark_connected(session)
if sendq then
session.log("debug", "sending %d queued stanzas across new outgoing connection to %s", #sendq, session.to_host);
local send = session.sends2s;
- for i, data in ipairs(sendq) do
- send(data[1]);
+ for i, stanza in ipairs(sendq) do
+ send(stanza);
sendq[i] = nil;
end
session.sendq = nil;
@@ -935,6 +929,16 @@ local function friendly_cert_error(session) --> string
elseif cert_errors:contains("self signed certificate") then
return "is self-signed";
end
+
+ local chain_errors = set.new(session.cert_chain_errors[2]);
+ for i, e in pairs(session.cert_chain_errors) do
+ if i > 2 then chain_errors:add_list(e); end
+ end
+ if chain_errors:contains("certificate has expired") then
+ return "has an expired certificate chain";
+ elseif chain_errors:contains("No matching DANE TLSA records") then
+ return "does not match any DANE TLSA records";
+ end
end
return "is not trusted"; -- for some other reason
elseif session.cert_identity_status == "invalid" then
diff --git a/plugins/mod_smacks.lua b/plugins/mod_smacks.lua
index ce59248e..0d2016fc 100644
--- a/plugins/mod_smacks.lua
+++ b/plugins/mod_smacks.lua
@@ -2,7 +2,7 @@
--
-- Copyright (C) 2010-2015 Matthew Wild
-- Copyright (C) 2010 Waqas Hussain
--- Copyright (C) 2012-2021 Kim Alvefur
+-- Copyright (C) 2012-2022 Kim Alvefur
-- Copyright (C) 2012 Thijs Alkemade
-- Copyright (C) 2014 Florian Zeitz
-- Copyright (C) 2016-2020 Thilo Molitor
@@ -10,6 +10,7 @@
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
+-- TODO unify sendq and smqueue
local tonumber = tonumber;
local tostring = tostring;
@@ -322,26 +323,20 @@ end
module:hook_tag(xmlns_sm2, "enable", function (session, stanza) return handle_enable(session, stanza, xmlns_sm2); end, 100);
module:hook_tag(xmlns_sm3, "enable", function (session, stanza) return handle_enable(session, stanza, xmlns_sm3); end, 100);
-module:hook_tag("http://etherx.jabber.org/streams", "features",
- function (session, stanza)
- -- Needs to be done after flushing sendq since those aren't stored as
- -- stanzas and counting them is weird.
- -- TODO unify sendq and smqueue
- timer.add_task(1e-6, function ()
- if can_do_smacks(session) then
- if stanza:get_child("sm", xmlns_sm3) then
- session.sends2s(st.stanza("enable", sm3_attr));
- session.smacks = xmlns_sm3;
- elseif stanza:get_child("sm", xmlns_sm2) then
- session.sends2s(st.stanza("enable", sm2_attr));
- session.smacks = xmlns_sm2;
- else
- return;
- end
- wrap_session_out(session, false);
- end
- end);
- end);
+module:hook_tag("http://etherx.jabber.org/streams", "features", function(session, stanza)
+ if can_do_smacks(session) then
+ session.smacks_feature = stanza:get_child("sm", xmlns_sm3) or stanza:get_child("sm", xmlns_sm2);
+ end
+end);
+
+module:hook("s2sout-established", function (event)
+ local session = event.session;
+ if not session.smacks_feature then return end
+
+ session.smacks = session.smacks_feature.attr.xmlns;
+ session.sends2s(st.stanza("enable", { xmlns = session.smacks }));
+ wrap_session_out(session, false);
+end);
function handle_enabled(session, stanza, xmlns_sm) -- luacheck: ignore 212/stanza
module:log("debug", "Enabling stream management");