diff options
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/mod_bosh.lua | 70 |
1 files changed, 41 insertions, 29 deletions
diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 47f0b3b4..6acdc23c 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -22,7 +22,6 @@ local initialize_filters = require "util.filters".initialize; local math_min = math.min; local xpcall, tostring, type = xpcall, tostring, type; local traceback = debug.traceback; -local runner = require"util.async".runner; local xmlns_streams = "http://etherx.jabber.org/streams"; local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; @@ -63,11 +62,13 @@ local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; local os_time = os.time; -- All sessions, and sessions that have no requests open -local sessions = module:shared("sessions"); +local sessions, inactive_sessions = module:shared("sessions", "inactive_sessions"); -- Used to respond to idle sessions (those with waiting requests) +local waiting_requests = module:shared("waiting_requests"); function on_destroy_request(request) log("debug", "Request destroyed: %s", tostring(request)); + waiting_requests[request] = nil; local session = sessions[request.context.sid]; if session then local requests = session.requests; @@ -81,24 +82,9 @@ function on_destroy_request(request) -- If this session now has no requests open, mark it as inactive local max_inactive = session.bosh_max_inactive; if max_inactive and #requests == 0 then - if session.inactive_timer then - session.inactive_timer:stop(); - end - session.inactive_timer = module:add_timer(max_inactive, check_inactive, session, request.context, - "BOSH client silent for over "..max_inactive.." seconds"); + inactive_sessions[session] = os_time() + max_inactive; (session.log or log)("debug", "BOSH session marked as inactive (for %ds)", max_inactive); end - if session.bosh_wait_timer then - session.bosh_wait_timer:stop(); - session.bosh_wait_timer = nil; - end - end -end - -function check_inactive(now, session, context, reason) - if not sessions.destroyed then - sessions[context.sid] = nil; - sm_destroy_session(session, reason); end end @@ -132,7 +118,7 @@ function handle_POST(event) local headers = response.headers; headers.content_type = "text/xml; charset=utf-8"; - if cross_domain and request.headers.origin then + if cross_domain and event.request.headers.origin then set_cross_domain_headers(response); end @@ -141,9 +127,12 @@ function handle_POST(event) -- In particular, the streamopened() stream callback is where -- much of the session logic happens, because it's where we first -- get to see the 'sid' of this request. - if not stream:feed(body) then - module:log("warn", "Error parsing BOSH payload") - return 400; + local ok, err = stream:feed(body); + if not ok then + module:log("warn", "Error parsing BOSH payload; %s", err) + local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", + ["xmlns:stream"] = xmlns_streams, condition = "bad-request" }); + return tostring(close_reply); end -- Stanzas (if any) in the request have now been processed, and @@ -200,9 +189,13 @@ function handle_POST(event) else return true; -- Inform http server we shall reply later end + elseif response.finished then + return; -- A response has been sent already end module:log("warn", "Unable to associate request with a session (incomplete request?)"); - return 400; + local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", + ["xmlns:stream"] = xmlns_streams, condition = "item-not-found" }); + return tostring(close_reply) .. "\n"; end function after_bosh_wait(now, request, session) @@ -263,8 +256,16 @@ function stream_callbacks.streamopened(context, attr) -- New session request context.notopen = nil; -- Signals that we accept this opening tag - -- TODO: Sanity checks here (rid, to, known host, etc.) - if not hosts[attr.to] then + local to_host = nameprep(attr.to); + local rid = tonumber(attr.rid); + local wait = tonumber(attr.wait); + if not to_host then + log("debug", "BOSH client tried to connect to invalid host: %s", tostring(attr.to)); + local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", + ["xmlns:stream"] = xmlns_streams, condition = "improper-addressing" }); + response:send(tostring(close_reply)); + return; + elseif not hosts[to_host] then -- Unknown host log("debug", "BOSH client tried to connect to unknown host: %s", tostring(attr.to)); local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", @@ -272,12 +273,22 @@ function stream_callbacks.streamopened(context, attr) response:send(tostring(close_reply)); return; end + if not rid or (not wait and attr.wait or wait < 0 or wait % 1 ~= 0) then + log("debug", "BOSH client sent invalid rid or wait attributes: rid=%s, wait=%s", tostring(attr.rid), tostring(attr.wait)); + local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", + ["xmlns:stream"] = xmlns_streams, condition = "bad-request" }); + response:send(tostring(close_reply)); + return; + end + + rid = rid - 1; + wait = math_min(wait, bosh_max_wait); -- New session sid = new_uuid(); local session = { - type = "c2s_unauthed", conn = {}, sid = sid, rid = tonumber(attr.rid)-1, host = attr.to, - bosh_version = attr.ver, bosh_wait = math_min(attr.wait, bosh_max_wait), streamid = sid, + type = "c2s_unauthed", conn = {}, sid = sid, rid = rid-1, host = attr.to, + bosh_version = attr.ver, bosh_wait = wait, streamid = sid, bosh_hold = BOSH_DEFAULT_HOLD, bosh_max_inactive = BOSH_DEFAULT_INACTIVITY, requests = { }, send_buffer = {}, reset_stream = bosh_reset_stream, close = bosh_close_stream, dispatch_stanza = core_process_stanza, notopen = true, @@ -420,8 +431,9 @@ function stream_callbacks.error(context, error) log("debug", "Error parsing BOSH request payload; %s", error); if not context.sid then local response = context.response; - response.status_code = 400; - response:send(); + local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", + ["xmlns:stream"] = xmlns_streams, condition = "bad-request" }); + response:send(tostring(close_reply)); return; end |