X-Git-Url: https://git.enpas.org/?a=blobdiff_plain;f=plugins%2Fmod_s2s%2Fmod_s2s.lua;h=263f24c0b55c2b633810521f89abdcb1cc159507;hb=2812d8d444f3408276eba8108b2535a5446e82b3;hp=c628dc47cfc615d3de96f9a75dca1c356ea957b2;hpb=ad8eb26bb81e69acf30e47c80a0f0d8963dc4fde;p=prosody.git diff --git a/plugins/mod_s2s/mod_s2s.lua b/plugins/mod_s2s/mod_s2s.lua index c628dc47..263f24c0 100644 --- a/plugins/mod_s2s/mod_s2s.lua +++ b/plugins/mod_s2s/mod_s2s.lua @@ -1,7 +1,7 @@ -- Prosody IM -- Copyright (C) 2008-2010 Matthew Wild -- Copyright (C) 2008-2010 Waqas Hussain --- +-- -- This project is MIT/X11 licensed. Please see the -- COPYING file in the source package for more information. -- @@ -150,15 +150,22 @@ function module.add_host(module) module:hook("route/remote", route_to_new_session, -10); 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) + 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 + mark_connected(session); + end + end, -1); end -- Stream is authorised, and ready for normal stanzas function mark_connected(session) local sendq, send = session.sendq, session.sends2s; - + local from, to = session.from_host, session.to_host; - - session.log("info", "%s s2s connection %s->%s complete", session.direction, from, to); + + session.log("info", "%s s2s connection %s->%s complete", session.direction:gsub("^.", string.upper), from, to); local event_data = { session = session }; if session.type == "s2sout" then @@ -173,7 +180,7 @@ function mark_connected(session) fire_global_event("s2sin-established", event_data); hosts[to].events.fire_event("s2sin-established", event_data); end - + if session.direction == "outgoing" then if sendq then session.log("debug", "sending %d queued stanzas across new outgoing connection to %s", #sendq, session.to_host); @@ -183,7 +190,7 @@ function mark_connected(session) end session.sendq = nil; end - + session.ip_hosts = nil; session.srv_hosts = nil; end @@ -218,9 +225,12 @@ function make_authenticated(event) return false; end session.log("debug", "connection %s->%s is now authenticated for %s", session.from_host, session.to_host, host); - - mark_connected(session); - + + if (session.type == "s2sout" and session.external_auth ~= "succeeded") or session.type == "s2sin" then + -- Stream either used dialback for authentication or is an incoming stream. + mark_connected(session); + end + return true; end @@ -277,18 +287,18 @@ local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; function stream_callbacks.streamopened(session, attr) local send = session.sends2s; - + session.version = tonumber(attr.version) or 0; - + -- TODO: Rename session.secure to session.encrypted if session.secure == false then session.secure = true; + session.encrypted = true; local sock = session.conn:socket(); if sock.info then local info = sock:info(); - (session.log or log)("info", "Stream encrypted (%s) with %s, authenticated with %s and exchanged keys with %s", - info.protocol, info.encryption, info.authentication, info.key); + (session.log or log)("info", "Stream encrypted (%s with %s)", info.protocol, info.cipher); session.compressed = info.compression; else (session.log or log)("info", "Stream encrypted"); @@ -298,7 +308,7 @@ function stream_callbacks.streamopened(session, attr) if session.direction == "incoming" then -- Send a reply stream header - + -- Validate to/from local to, from = nameprep(attr.to), nameprep(attr.from); if not to and attr.to then -- COMPAT: Some servers do not reliably set 'to' (especially on stream restarts) @@ -309,7 +319,7 @@ function stream_callbacks.streamopened(session, attr) session:close({ condition = "improper-addressing", text = "Invalid 'from' address" }); return; end - + -- Set session.[from/to]_host if they have not been set already and if -- this session isn't already authenticated if session.type == "s2sin_unauthed" and from and not session.from_host then @@ -324,10 +334,10 @@ function stream_callbacks.streamopened(session, attr) session:close({ condition = "improper-addressing", text = "New stream 'to' attribute does not match original" }); return; end - + -- For convenience we'll put the sanitised values into these variables to, from = session.to_host, session.from_host; - + session.streamid = uuid_gen(); (session.log or log)("debug", "Incoming s2s received %s", st.stanza("stream:stream", attr):top_tag()); if to then @@ -362,13 +372,13 @@ function stream_callbacks.streamopened(session, attr) session:open_stream(session.to_host, session.from_host) if session.version >= 1.0 then local features = st.stanza("stream:features"); - + if to then hosts[to].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", from or "unknown host"); + (session.log or log)("warn", "No 'to' on stream header from %s means we can't offer any features", from or session.ip or "unknown host"); end - + log("debug", "Sending stream features: %s", tostring(features)); send(features); end @@ -396,7 +406,7 @@ function stream_callbacks.streamopened(session, attr) end end session.send_buffer = nil; - + -- If server is pre-1.0, don't wait for features, just do dialback if session.version < 1.0 then if not session.dialback_verifying then @@ -467,7 +477,7 @@ local function session_close(session, reason, remote_reason) end if reason then -- nil == no err, initiated by us, false == initiated by remote if type(reason) == "string" then -- assume stream error - log("debug", "Disconnecting %s[%s], is: %s", session.host or "(unknown host)", session.type, reason); + log("debug", "Disconnecting %s[%s], is: %s", session.host or session.ip or "(unknown host)", session.type, reason); session.sends2s(st.stanza("stream:error"):tag(reason, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' })); elseif type(reason) == "table" then if reason.condition then @@ -478,7 +488,7 @@ local function session_close(session, reason, remote_reason) if reason.extra then stanza:add_child(reason.extra); end - log("debug", "Disconnecting %s[%s], is: %s", session.host or "(unknown host)", session.type, tostring(stanza)); + log("debug", "Disconnecting %s[%s], is: %s", session.host or session.ip or "(unknown host)", session.type, tostring(stanza)); session.sends2s(stanza); elseif reason.name then -- a stanza log("debug", "Disconnecting %s->%s[%s], is: %s", session.from_host or "(unknown host)", session.to_host or "(unknown host)", session.type, tostring(reason)); @@ -489,10 +499,10 @@ local function session_close(session, reason, remote_reason) session.sends2s(""); function session.sends2s() return false; end - + local reason = remote_reason or (reason and (reason.text or reason.condition)) or reason; - session.log("info", "%s s2s stream %s->%s closed: %s", session.direction, session.from_host or "(unknown host)", session.to_host or "(unknown host)", reason or "stream closed"); - + session.log("info", "%s s2s stream %s->%s closed: %s", session.direction:gsub("^.", string.upper), session.from_host or "(unknown host)", session.to_host or "(unknown host)", reason or "stream closed"); + -- Authenticated incoming stream may still be sending us stanzas, so wait for from remote local conn = session.conn; if reason == nil and not session.notopen and session.type == "s2sin" then @@ -510,38 +520,26 @@ local function session_close(session, reason, remote_reason) end end -function session_open_stream(session, from, to) - local attr = { - ["xmlns:stream"] = 'http://etherx.jabber.org/streams', - xmlns = 'jabber:server', - version = session.version and (session.version > 0 and "1.0" or nil), - ["xml:lang"] = 'en', - id = session.streamid, - from = from, to = to, - } +function session_stream_attrs(session, from, to, attr) if not from or (hosts[from] and hosts[from].modules.dialback) then attr["xmlns:db"] = 'jabber:server:dialback'; end - - session.sends2s(""); - session.sends2s(st.stanza("stream:stream", attr):top_tag()); - return true; end -- Session initialization logic shared by incoming and outgoing local function initialize_session(session) local stream = new_xmpp_stream(session, stream_callbacks); session.stream = stream; - + session.notopen = true; - + function session.reset_stream() session.notopen = true; session.stream:reset(); end - session.open_stream = session_open_stream; - + session.stream_attrs = session_stream_attrs; + local filter = session.filter; function session.data(data) data = filter("bytes/in", data); @@ -596,7 +594,7 @@ function listener.onconnect(conn) end end end - + initialize_session(session); else -- Outgoing session connected session:open_stream(session.from_host, session.to_host); @@ -610,7 +608,7 @@ function listener.onincoming(conn, data) session.data(data); end end - + function listener.onstatus(conn, status) if status == "ssl-handshake-complete" then local session = sessions[conn]; @@ -658,9 +656,9 @@ function check_auth_policy(event) elseif must_secure and insecure_domains[host] then must_secure = false; end - + if must_secure and (session.cert_chain_status ~= "valid" or session.cert_identity_status ~= "valid") then - module:log("warn", "Forbidding insecure connection to/from %s", host); + module:log("warn", "Forbidding insecure connection to/from %s", host or session.ip or "(unknown host)"); if session.direction == "incoming" then session:close({ condition = "not-authorized", text = "Your server's certificate is invalid, expired, or not trusted by "..session.to_host }); else -- Close outgoing connections without warning