aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/mod_s2s.lua
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/mod_s2s.lua')
-rw-r--r--plugins/mod_s2s.lua87
1 files changed, 61 insertions, 26 deletions
diff --git a/plugins/mod_s2s.lua b/plugins/mod_s2s.lua
index 300a747e..6d1a76a0 100644
--- a/plugins/mod_s2s.lua
+++ b/plugins/mod_s2s.lua
@@ -146,17 +146,17 @@ 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] and stanza.attr.type ~= "error" and stanza.attr.type ~= "result" 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);
+ else
+ (session.log or log)("debug", "Not eligible for bouncing, discarding %s", stanza:top_tag());
end
sendq[i] = nil;
end
@@ -182,15 +182,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 +211,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
@@ -251,9 +247,26 @@ function module.add_host(module)
end
module:hook("route/remote", route_to_existing_session, -1);
module:hook("route/remote", route_to_new_session, -10);
+ module:hook("s2sout-stream-features", function (event)
+ if stanza_size_limit then
+ event.features:tag("limits", { xmlns = "urn:xmpp:stream-limits:0" })
+ :text_tag("max-size", string.format("%d", stanza_size_limit)):up();
+ end
+ end);
+ module:hook_tag("urn:xmpp:bidi", "bidi", function(session, stanza)
+ -- Advertising features on bidi connections where no <stream:features> is sent in the other direction
+ local limits = stanza:get_child("limits", "urn:xmpp:stream-limits:0");
+ if limits then
+ session.outgoing_stanza_size_limit = tonumber(limits:get_child_text("max-size"));
+ end
+ end, 100);
module:hook("s2s-authenticated", make_authenticated, -1);
module:hook("s2s-read-timeout", keepalive, -1);
module:hook_stanza("http://etherx.jabber.org/streams", "features", function (session, stanza) -- luacheck: ignore 212/stanza
+ local limits = stanza:get_child("limits", "urn:xmpp:stream-limits:0");
+ if limits then
+ session.outgoing_stanza_size_limit = tonumber(limits:get_child_text("max-size"));
+ end
if session.type == "s2sout" then
-- Stream is authenticated and we are seem to be done with feature negotiation,
-- so the stream is ready for stanzas. RFC 6120 Section 4.3
@@ -279,7 +292,7 @@ function module.add_host(module)
function module.unload()
if module.reloading then return end
for _, session in pairs(sessions) do
- if session.to_host == module.host or session.from_host == module.host then
+ if session.host == module.host then
session:close("host-gone");
end
end
@@ -324,8 +337,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;
@@ -389,10 +402,10 @@ end
--- Helper to check that a session peer's certificate is valid
local function check_cert_status(session)
local host = session.direction == "outgoing" and session.to_host or session.from_host
- local conn = session.conn:socket()
+ local conn = session.conn
local cert
- if conn.getpeercertificate then
- cert = conn:getpeercertificate()
+ if conn.ssl_peercertificate then
+ cert = conn:ssl_peercertificate()
end
return module:fire_event("s2s-check-certificate", { host = host, session = session, cert = cert });
@@ -404,8 +417,7 @@ local function session_secure(session)
session.secure = true;
session.encrypted = true;
- local sock = session.conn:socket();
- local info = sock.info and sock:info();
+ local info = session.conn:ssl_info();
if type(info) == "table" then
(session.log or log)("info", "Stream encrypted (%s with %s)", info.protocol, info.cipher);
session.compressed = info.compression;
@@ -434,7 +446,8 @@ function stream_callbacks._streamopened(session, attr)
session.had_stream = true; -- Had a stream opened at least once
-- TODO: Rename session.secure to session.encrypted
- if session.secure == false then
+ if session.secure == false then -- Set by mod_tls during STARTTLS handshake
+ session.starttls = "completed";
session_secure(session);
end
@@ -522,6 +535,12 @@ function stream_callbacks._streamopened(session, attr)
end
if ( session.type == "s2sin" or session.type == "s2sout" ) or features.tags[1] then
+ if stanza_size_limit then
+ features:reset();
+ features:tag("limits", { xmlns = "urn:xmpp:stream-limits:0" })
+ :text_tag("max-size", string.format("%d", stanza_size_limit)):up();
+ end
+
log("debug", "Sending stream features: %s", features);
session.sends2s(features);
else
@@ -756,6 +775,7 @@ local function initialize_session(session)
local w = conn.write;
if conn:ssl() then
+ -- Direct TLS was used
session_secure(session);
end
@@ -766,6 +786,11 @@ local function initialize_session(session)
end
if t then
t = filter("bytes/out", tostring(t));
+ if session.outgoing_stanza_size_limit and #t > session.outgoing_stanza_size_limit then
+ log("warn", "Attempt to send a stanza exceeding session limit of %dB (%dB)!", session.outgoing_stanza_size_limit, #t);
+ -- TODO Pass identifiable error condition back to allow appropriate handling
+ return false
+ end
if t then
return w(conn, t);
end
@@ -935,6 +960,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