aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Wild <mwild1@gmail.com>2014-03-30 09:14:39 +0100
committerMatthew Wild <mwild1@gmail.com>2014-03-30 09:14:39 +0100
commit16a37370eb93573c6d561d08ac79b0479978fa24 (patch)
tree20e9677a27aa0ca0959dac167a17c870d5921a20
parent5273af7c22f99bcfcb856fc94e5ff78296b74fb9 (diff)
downloadprosody-16a37370eb93573c6d561d08ac79b0479978fa24.tar.gz
prosody-16a37370eb93573c6d561d08ac79b0479978fa24.zip
util.xmppstream: Implement stanza size limiting, default limit 10MB
-rw-r--r--util/xmppstream.lua86
1 files changed, 80 insertions, 6 deletions
diff --git a/util/xmppstream.lua b/util/xmppstream.lua
index 4909678c..9cdd6471 100644
--- a/util/xmppstream.lua
+++ b/util/xmppstream.lua
@@ -6,7 +6,6 @@
-- COPYING file in the source package for more information.
--
-
local lxp = require "lxp";
local st = require "util.stanza";
local stanza_mt = st.stanza_mt;
@@ -20,6 +19,10 @@ local setmetatable = setmetatable;
-- COMPAT: w/LuaExpat 1.1.0
local lxp_supports_doctype = pcall(lxp.new, { StartDoctypeDecl = false });
+local lxp_supports_xmldecl = pcall(lxp.new, { XmlDecl = false });
+local lxp_supports_bytecount = not not lxp.new({}).getcurrentbytecount;
+
+local default_stanza_size_limit = 1024*1024*10; -- 10MB
module "xmppstream"
@@ -40,13 +43,16 @@ local ns_pattern = "^([^"..ns_separator.."]*)"..ns_separator.."?(.*)$";
_M.ns_separator = ns_separator;
_M.ns_pattern = ns_pattern;
-function new_sax_handlers(session, stream_callbacks)
+local function dummy_cb() end
+
+function new_sax_handlers(session, stream_callbacks, cb_handleprogress)
local xml_handlers = {};
local cb_streamopened = stream_callbacks.streamopened;
local cb_streamclosed = stream_callbacks.streamclosed;
local cb_error = stream_callbacks.error or function(session, e, stanza) error("XML stream error: "..tostring(e)..(stanza and ": "..tostring(stanza) or ""),2); end;
local cb_handlestanza = stream_callbacks.handlestanza;
+ cb_handleprogress = cb_handleprogress or dummy_cb;
local stream_ns = stream_callbacks.stream_ns or xmlns_streams;
local stream_tag = stream_callbacks.stream_tag or "stream";
@@ -59,6 +65,7 @@ function new_sax_handlers(session, stream_callbacks)
local stack = {};
local chardata, stanza = {};
+ local stanza_size = 0;
local non_streamns_depth = 0;
function xml_handlers:StartElement(tagname, attr)
if stanza and #chardata > 0 then
@@ -87,10 +94,17 @@ function new_sax_handlers(session, stream_callbacks)
end
if not stanza then --if we are not currently inside a stanza
+ if lxp_supports_bytecount then
+ stanza_size = self:getcurrentbytecount();
+ end
if session.notopen then
if tagname == stream_tag then
non_streamns_depth = 0;
if cb_streamopened then
+ if lxp_supports_bytecount then
+ cb_handleprogress(stanza_size);
+ stanza_size = 0;
+ end
cb_streamopened(session, attr);
end
else
@@ -105,6 +119,9 @@ function new_sax_handlers(session, stream_callbacks)
stanza = setmetatable({ name = name, attr = attr, tags = {} }, stanza_mt);
else -- we are inside a stanza, so add a tag
+ if lxp_supports_bytecount then
+ stanza_size = stanza_size + self:getcurrentbytecount();
+ end
t_insert(stack, stanza);
local oldstanza = stanza;
stanza = setmetatable({ name = name, attr = attr, tags = {} }, stanza_mt);
@@ -112,12 +129,45 @@ function new_sax_handlers(session, stream_callbacks)
t_insert(oldstanza.tags, stanza);
end
end
+ if lxp_supports_xmldecl then
+ function xml_handlers:XmlDecl(version, encoding, standalone)
+ if lxp_supports_bytecount then
+ cb_handleprogress(self:getcurrentbytecount());
+ end
+ end
+ end
+ function xml_handlers:StartCdataSection()
+ if lxp_supports_bytecount then
+ if stanza then
+ stanza_size = stanza_size + self:getcurrentbytecount();
+ else
+ cb_handleprogress(self:getcurrentbytecount());
+ end
+ end
+ end
+ function xml_handlers:EndCdataSection()
+ if lxp_supports_bytecount then
+ if stanza then
+ stanza_size = stanza_size + self:getcurrentbytecount();
+ else
+ cb_handleprogress(self:getcurrentbytecount());
+ end
+ end
+ end
function xml_handlers:CharacterData(data)
if stanza then
+ if lxp_supports_bytecount then
+ stanza_size = stanza_size + #data --self:getcurrentbytecount();
+ end
t_insert(chardata, data);
+ elseif lxp_supports_bytecount then
+ cb_handleprogress(#data--[[self:getcurrentbytecount()]]);
end
end
function xml_handlers:EndElement(tagname)
+ if lxp_supports_bytecount then
+ stanza_size = stanza_size + self:getcurrentbytecount()
+ end
if non_streamns_depth > 0 then
non_streamns_depth = non_streamns_depth - 1;
end
@@ -129,6 +179,10 @@ function new_sax_handlers(session, stream_callbacks)
end
-- Complete stanza
if #stack == 0 then
+ if lxp_supports_bytecount then
+ cb_handleprogress(stanza_size);
+ end
+ stanza_size = 0;
if tagname ~= stream_error_tag then
cb_handlestanza(session, stanza);
else
@@ -159,7 +213,7 @@ function new_sax_handlers(session, stream_callbacks)
xml_handlers.ProcessingInstruction = restricted_handler;
local function reset()
- stanza, chardata = nil, {};
+ stanza, chardata, stanza_size = nil, {}, 0;
stack = {};
end
@@ -170,8 +224,20 @@ function new_sax_handlers(session, stream_callbacks)
return xml_handlers, { reset = reset, set_session = set_session };
end
-function new(session, stream_callbacks)
- local handlers, meta = new_sax_handlers(session, stream_callbacks);
+function new(session, stream_callbacks, stanza_size_limit)
+ -- Used to track parser progress (e.g. to enforce size limits)
+ local n_outstanding_bytes = 0;
+ local handle_progress;
+ if lxp_supports_bytecount then
+ function handle_progress(n_parsed_bytes)
+ n_outstanding_bytes = n_outstanding_bytes - n_parsed_bytes;
+ end
+ stanza_size_limit = stanza_size_limit or default_stanza_size_limit;
+ elseif stanza_size_limit then
+ error("Stanza size limits are not supported on this version of LuaExpat")
+ end
+
+ local handlers, meta = new_sax_handlers(session, stream_callbacks, handle_progress);
local parser = new_parser(handlers, ns_separator);
local parse = parser.parse;
@@ -179,10 +245,18 @@ function new(session, stream_callbacks)
reset = function ()
parser = new_parser(handlers, ns_separator);
parse = parser.parse;
+ n_outstanding_bytes = 0;
meta.reset();
end,
feed = function (self, data)
- return parse(parser, data);
+ if lxp_supports_bytecount then
+ n_outstanding_bytes = n_outstanding_bytes + #data;
+ end
+ local ok, err = parse(parser, data);
+ if lxp_supports_bytecount and n_outstanding_bytes > stanza_size_limit then
+ return nil, "stanza-too-large";
+ end
+ return ok, err;
end,
set_session = meta.set_session;
};