diff options
Diffstat (limited to 'core/s2smanager.lua')
-rw-r--r-- | core/s2smanager.lua | 84 |
1 files changed, 53 insertions, 31 deletions
diff --git a/core/s2smanager.lua b/core/s2smanager.lua index bfa3069a..16ede7b6 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -36,8 +36,6 @@ local log = logger_init("s2smanager"); local sha256_hash = require "util.hashes".sha256; -local dialback_secret = uuid_gen(); - local adns, dns = require "net.adns", require "net.dns"; local config = require "core.configmanager"; local connect_timeout = config.get("*", "core", "s2s_timeout") or 60; @@ -54,7 +52,7 @@ function compare_srv_priorities(a,b) return a.priority < b.priority or (a.priority == b.priority and a.weight > b.weight); end -local function bounce_sendq(session) +local function bounce_sendq(session, reason) local sendq = session.sendq; if sendq then session.log("info", "sending error replies for "..#sendq.." queued stanzas because of failed outgoing connection to "..tostring(session.to_host)); @@ -72,6 +70,9 @@ local function bounce_sendq(session) reply.attr.type = "error"; reply:tag("error", {type = "cancel"}) :tag("remote-server-not-found", {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):up(); + if reason then + reply:tag("text", {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):text("Connection failed: "..reason):up(); + end core_process_stanza(dummy, reply); end sendq[i] = nil; @@ -134,7 +135,7 @@ function new_incoming(conn) open_sessions = open_sessions + 1; local w, log = conn.write, logger_init("s2sin"..tostring(conn):match("[a-f0-9]+$")); session.log = log; - session.sends2s = function (t) log("debug", "sending: %s", tostring(t)); w(tostring(t)); end + session.sends2s = function (t) log("debug", "sending: %s", t.top_tag and t:top_tag() or t:match("^([^>]*>?)")); w(conn, tostring(t)); end incoming_s2s[session] = true; add_task(connect_timeout, function () if session.conn ~= conn or @@ -142,16 +143,17 @@ function new_incoming(conn) return; -- Ok, we're connect[ed|ing] end -- Not connected, need to close session and clean up - (session.log or log)("warn", "Destroying incomplete session %s->%s due to inactivity", + (session.log or log)("warn", "Destroying incomplete session %s->%s due to inactivity", session.from_host or "(unknown)", session.to_host or "(unknown)"); session:close("connection-timeout"); end); return session; end -function new_outgoing(from_host, to_host) - local host_session = { to_host = to_host, from_host = from_host, host = from_host, - notopen = true, type = "s2sout_unauthed", direction = "outgoing" }; +function new_outgoing(from_host, to_host, connect) + local host_session = { to_host = to_host, from_host = from_host, host = from_host, + notopen = true, type = "s2sout_unauthed", direction = "outgoing", + open_stream = session_open_stream }; hosts[from_host].s2sout[to_host] = host_session; @@ -162,10 +164,12 @@ function new_outgoing(from_host, to_host) host_session.log = log; end - -- Kick the connection attempting machine - attempt_connection(host_session); + if connect ~= false then + -- Kick the connection attempting machine into life + attempt_connection(host_session); + end - if not host_session.sends2s then + if not host_session.sends2s then -- A sends2s which buffers data (until the stream is opened) -- note that data in this buffer will be sent before the stream is authed -- and will not be ack'd in any way, successful or otherwise @@ -179,7 +183,6 @@ function new_outgoing(from_host, to_host) buffer[#buffer+1] = data; log("debug", "Buffered item %d: %s", #buffer, tostring(data)); end - end return host_session; @@ -224,7 +227,7 @@ function attempt_connection(host_session, err) if not ok then if not attempt_connection(host_session, err) then -- No more attempts will be made - destroy_session(host_session); + destroy_session(host_session, err); end end end, "_xmpp-server._tcp."..connect_host..".", "SRV"); @@ -284,7 +287,7 @@ function try_connect(host_session, connect_host, connect_port) log("debug", "DNS lookup failed to get a response for %s", connect_host); if not attempt_connection(host_session, "name resolution failed") then -- Retry if we can log("debug", "No other records to try for %s - destroying", host_session.to_host); - destroy_session(host_session); -- End of the line, we can't + destroy_session(host_session, "DNS resolution failed"); -- End of the line, we can't end end end, connect_host, "A", "IN"); @@ -295,12 +298,12 @@ function try_connect(host_session, connect_host, connect_port) adns.cancel(handle, true); end end); - + return true; end function make_connect(host_session, connect_host, connect_port) - host_session.log("info", "Beginning new connection attempt to %s (%s:%d)", host_session.to_host, connect_host, connect_port); + (host_session.log or log)("info", "Beginning new connection attempt to %s (%s:%d)", host_session.to_host, connect_host, connect_port); -- Ok, we're going to try to connect local from_host, to_host = host_session.from_host, host_session.to_host; @@ -320,7 +323,7 @@ function make_connect(host_session, connect_host, connect_port) end local cl = connlisteners_get("xmppserver"); - conn = wrapclient(conn, connect_host, connect_port, cl, cl.default_mode or 1, hosts[from_host].ssl_ctx, false ); + conn = wrapclient(conn, connect_host, connect_port, cl, cl.default_mode or 1 ); host_session.conn = conn; -- Register this outgoing connection so that xmppserver_listener knows about it @@ -328,9 +331,10 @@ function make_connect(host_session, connect_host, connect_port) cl.register_outgoing(conn, host_session); local w, log = conn.write, host_session.log; - host_session.sends2s = function (t) log("debug", "sending: %s", tostring(t)); w(tostring(t)); end + host_session.sends2s = function (t) log("debug", "sending: %s", (t.top_tag and t:top_tag()) or t:match("^[^>]*>?")); w(conn, tostring(t)); end + + host_session:open_stream(from_host, to_host); - conn.write(format([[<stream:stream xmlns='jabber:server' xmlns:db='jabber:server:dialback' xmlns:stream='http://etherx.jabber.org/streams' from='%s' to='%s' version='1.0' xml:lang='en'>]], from_host, to_host)); log("debug", "Connection attempt in progress..."); add_task(connect_timeout, function () if host_session.conn ~= conn or @@ -339,13 +343,20 @@ function make_connect(host_session, connect_host, connect_port) return; -- Ok, we're connect[ed|ing] end -- Not connected, need to close session and clean up - (host_session.log or log)("warn", "Destroying incomplete session %s->%s due to inactivity", + (host_session.log or log)("warn", "Destroying incomplete session %s->%s due to inactivity", host_session.from_host or "(unknown)", host_session.to_host or "(unknown)"); host_session:close("connection-timeout"); end); return true; end +function session_open_stream(session, from, to) + session.sends2s(st.stanza("stream:stream", { + xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', + ["xmlns:stream"]='http://etherx.jabber.org/streams', + from=from, to=to, version='1.0', ["xml:lang"]='en'}):top_tag()); +end + function streamopened(session, attr) local send = session.sends2s; @@ -357,7 +368,6 @@ function streamopened(session, attr) end if session.version >= 1.0 and not (attr.to and attr.from) then - (session.log or log)("warn", "Remote of stream "..(session.from_host or "(unknown)").."->"..(session.to_host or "(unknown)") .." failed to specify to (%s) and/or from (%s) hostname as per RFC", tostring(attr.to), tostring(attr.from)); end @@ -369,19 +379,19 @@ function streamopened(session, attr) session.streamid = uuid_gen(); (session.log or log)("debug", "incoming s2s received <stream:stream>"); - send("<?xml version='1.0'?>"); - send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', - ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host, version=(session.version > 0 and "1.0" or nil) }):top_tag()); if session.to_host and not hosts[session.to_host] then -- Attempting to connect to a host we don't serve session:close({ condition = "host-unknown"; text = "This host does not serve "..session.to_host }); return; end + send("<?xml version='1.0'?>"); + send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', + ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host, to=session.from_host, version=(session.version > 0 and "1.0" or nil) }):top_tag()); if session.version >= 1.0 then local features = st.stanza("stream:features"); - + if session.to_host then - hosts[session.to_host].events.fire_event("s2s-stream-features", { session = session, features = features }); + hosts[session.to_host].events.fire_event("s2s-stream-features", { origin = session, features = features }); else (session.log or log)("warn", "No 'to' on stream header from %s means we can't offer any features", session.from_host or "unknown host"); end @@ -396,7 +406,7 @@ function streamopened(session, attr) -- Send unauthed buffer -- (stanzas which are fine to send before dialback) - -- Note that this is *not* the stanza queue (which + -- Note that this is *not* the stanza queue (which -- we can only send if auth succeeds) :) local send_buffer = session.send_buffer; if send_buffer and #send_buffer > 0 then @@ -418,7 +428,6 @@ function streamopened(session, attr) end end end - session.notopen = nil; end @@ -438,7 +447,7 @@ function initiate_dialback(session) end function generate_dialback(id, to, from) - return sha256_hash(id..to..from..dialback_secret, true); + return sha256_hash(id..to..from..hosts[from].dialback_secret, true); end function verify_dialback(id, to, from, key) @@ -446,6 +455,16 @@ function verify_dialback(id, to, from, key) end function make_authenticated(session, host) + if not session.secure then + local local_host = session.direction == "incoming" and session.to_host or session.from_host; + if config.get(local_host, "core", "s2s_require_encryption") then + session:close({ + condition = "policy-violation", + text = "Encrypted server-to-server communication is required but was not " + ..((session.direction == "outgoing" and "offered") or "used") + }); + end + end if session.type == "s2sout_unauthed" then session.type = "s2sout"; elseif session.type == "s2sin_unauthed" then @@ -491,12 +510,14 @@ function mark_connected(session) end end -function destroy_session(session) +local function null_data_handler(conn, data) log("debug", "Discarding data from destroyed s2s session: %s", data); end + +function destroy_session(session, reason) (session.log or log)("info", "Destroying "..tostring(session.direction).." session "..tostring(session.from_host).."->"..tostring(session.to_host)); if session.direction == "outgoing" then hosts[session.from_host].s2sout[session.to_host] = nil; - bounce_sendq(session); + bounce_sendq(session, reason); elseif session.direction == "incoming" then incoming_s2s[session] = nil; end @@ -506,6 +527,7 @@ function destroy_session(session) session[k] = nil; end end + session.data = null_data_handler; end return _M; |