X-Git-Url: https://git.enpas.org/?a=blobdiff_plain;f=core%2Fs2smanager.lua;h=d8ceb4efeb17ca58694a4d0a611cf6f39bd693ca;hb=090bf617e12f1a84c0d1d04917f771c9684a5304;hp=20d932e8fb6a8271cec2fa26e2b7267e3990fd26;hpb=73fca41d6807b6fb4606e513a09f35b1415be5a9;p=prosody.git diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 20d932e8..d8ceb4ef 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; @@ -52,7 +50,7 @@ module "s2smanager" local function compare_srv_priorities(a,b) return a.priority < b.priority or 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)); @@ -70,6 +68,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; @@ -132,7 +133,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 @@ -140,16 +141,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; @@ -160,10 +162,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 @@ -177,7 +181,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; @@ -222,7 +225,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"); @@ -282,7 +285,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"); @@ -293,7 +296,7 @@ function try_connect(host_session, connect_host, connect_port) adns.cancel(handle, true); end end); - + return true; end @@ -304,6 +307,11 @@ function make_connect(host_session, connect_host, connect_port) local from_host, to_host = host_session.from_host, host_session.to_host; local conn, handler = socket.tcp() + + if not conn then + log("warn", "Failed to create outgoing connection, system error: %s", handler); + return false, handler; + end conn:settimeout(0); local success, err = conn:connect(connect_host, connect_port); @@ -313,7 +321,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 @@ -321,9 +329,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([[]], from_host, to_host)); log("debug", "Connection attempt in progress..."); add_task(connect_timeout, function () if host_session.conn ~= conn or @@ -332,13 +341,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; @@ -350,7 +366,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 @@ -362,17 +377,17 @@ function streamopened(session, attr) session.streamid = uuid_gen(); (session.log or log)("debug", "incoming s2s received "); - send(""); - 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(""); + 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 }); else @@ -389,7 +404,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 @@ -411,7 +426,6 @@ function streamopened(session, attr) end end end - session.notopen = nil; end @@ -431,7 +445,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) @@ -439,6 +453,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 @@ -484,12 +508,12 @@ function mark_connected(session) end end -function destroy_session(session) +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