Mercurial > prosody-modules
changeset 2776:3092ae96c1f0
mod_compression: Rename to mod_compression_unsafe and add security note
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Tue, 26 Sep 2017 14:08:39 +0100 |
parents | 8407137c0a3b |
children | 55a7ef2fb628 |
files | mod_compression/README.markdown mod_compression/mod_compression.lua mod_compression_unsafe/README.markdown mod_compression_unsafe/mod_compression_unsafe.lua |
diffstat | 4 files changed, 255 insertions(+), 252 deletions(-) [+] |
line wrap: on
line diff
--- a/mod_compression/README.markdown Tue Sep 26 12:35:25 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -While the bandwidth usage of XMPP isn't that much, compressing the data -sent to/from your server can give significant benefits to those on slow -connections, such as dial-up or mobile networks. Prosody supports -compression for client-to-server (if your client supports it) and -server-to-server streams using the mod\_compression plugin. - -# Details - -mod\_compression implements [XEP-0138], and supports the zlib compression -algorithm. - -## Dependencies - -The XMPP protocol specifies that all clients and servers supporting -compression must support the "zlib" compression method, and this is what -Prosody uses. However you will need to install zlib support for Lua on -your system. There are different ways of doing this depending on your -system. If in doubt whether it is installed correctly, the command -`lua -lzlib` in a console should open a Lua prompt with no errors. - -Debian/Ubuntu -: `apt-get install lua-zlib` - -LuaRocks -: `luarocks install lua-zlib` - -Source -: <https://github.com/brimworks/lua-zlib> - -# Usage - -``` lua -modules_enabled = { - -- Other modules - "compression"; -- Enable mod_compression -} -``` - -## Configuration - -The compression level can be set using the `compression_level` option -which can be a number from 1 to 9. Higher compression levels will use -more resources but less bandwidth. - -## Example - -``` lua -modules_enabled = { - -- Other modules - "compression"; -- Enable mod_compression -} - -compression_level = 5 -```
--- a/mod_compression/mod_compression.lua Tue Sep 26 12:35:25 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,198 +0,0 @@ --- Prosody IM --- Copyright (C) 2009-2012 Tobias Markmann --- --- This project is MIT/X11 licensed. Please see the --- COPYING file in the source package for more information. --- - -local st = require "util.stanza"; -local zlib = require "zlib"; -local pcall = pcall; -local tostring = tostring; - -local xmlns_compression_feature = "http://jabber.org/features/compress" -local xmlns_compression_protocol = "http://jabber.org/protocol/compress" -local xmlns_stream = "http://etherx.jabber.org/streams"; -local compression_stream_feature = st.stanza("compression", {xmlns=xmlns_compression_feature}):tag("method"):text("zlib"):up(); -local add_filter = require "util.filters".add_filter; - -local compression_level = module:get_option_number("compression_level", 7); - -if not compression_level or compression_level < 1 or compression_level > 9 then - module:log("warn", "Invalid compression level in config: %s", tostring(compression_level)); - module:log("warn", "Module loading aborted. Compression won't be available."); - return; -end - -module:hook("stream-features", function(event) - local origin, features = event.origin, event.features; - if not origin.compressed and origin.type == "c2s" then - features:add_child(compression_stream_feature); - end -end); - -module:hook("s2s-stream-features", function(event) - local origin, features = event.origin, event.features; - if not origin.compressed and origin.type == "s2sin" then - features:add_child(compression_stream_feature); - end -end); - --- Hook to activate compression if remote server supports it. -module:hook_stanza(xmlns_stream, "features", - function (session, stanza) - if not session.compressed and session.type == "s2sout" then - -- does remote server support compression? - local comp_st = stanza:get_child("compression", xmlns_compression_feature); - if comp_st then - -- do we support the mechanism - for a in comp_st:childtags("method") do - local algorithm = a:get_text(); - if algorithm == "zlib" then - session.sends2s(st.stanza("compress", {xmlns=xmlns_compression_protocol}):tag("method"):text("zlib")) - session.log("debug", "Enabled compression using zlib.") - return true; - end - end - session.log("debug", "Remote server supports no compression algorithm we support.") - end - end - end -, 250); - - --- returns either nil or a fully functional ready to use inflate stream -local function get_deflate_stream(session) - local status, deflate_stream = pcall(zlib.deflate, compression_level); - if status == false then - local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed"); - (session.sends2s or session.send)(error_st); - session.log("error", "Failed to create zlib.deflate filter."); - module:log("error", "%s", tostring(deflate_stream)); - return - end - return deflate_stream -end - --- returns either nil or a fully functional ready to use inflate stream -local function get_inflate_stream(session) - local status, inflate_stream = pcall(zlib.inflate); - if status == false then - local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed"); - (session.sends2s or session.send)(error_st); - session.log("error", "Failed to create zlib.inflate filter."); - module:log("error", "%s", tostring(inflate_stream)); - return - end - return inflate_stream -end - --- setup compression for a stream -local function setup_compression(session, deflate_stream) - add_filter(session, "bytes/out", function(t) - local status, compressed, eof = pcall(deflate_stream, tostring(t), 'sync'); - if status == false then - module:log("warn", "%s", tostring(compressed)); - session:close({ - condition = "undefined-condition"; - text = compressed; - extra = st.stanza("failure", {xmlns="http://jabber.org/protocol/compress"}):tag("processing-failed"); - }); - return; - end - return compressed; - end); -end - --- setup decompression for a stream -local function setup_decompression(session, inflate_stream) - add_filter(session, "bytes/in", function(data) - local status, decompressed, eof = pcall(inflate_stream, data); - if status == false then - module:log("warn", "%s", tostring(decompressed)); - session:close({ - condition = "undefined-condition"; - text = decompressed; - extra = st.stanza("failure", {xmlns="http://jabber.org/protocol/compress"}):tag("processing-failed"); - }); - return; - end - return decompressed; - end); -end - -module:hook("stanza/http://jabber.org/protocol/compress:compressed", function(event) - local session = event.origin; - - if session.type == "s2sout" then - session.log("debug", "Activating compression...") - -- create deflate and inflate streams - local deflate_stream = get_deflate_stream(session); - if not deflate_stream then return true; end - - local inflate_stream = get_inflate_stream(session); - if not inflate_stream then return true; end - - -- setup compression for session.w - setup_compression(session, deflate_stream); - - -- setup decompression for session.data - setup_decompression(session, inflate_stream); - session:reset_stream(); - session:open_stream(session.from_host, session.to_host); - session.compressed = true; - return true; - end -end); - -module:hook("stanza/http://jabber.org/protocol/compress:failure", function(event) - local err = event.stanza:get_child(); - (event.origin.log or module._log)("warn", "Compression setup failed (%s)", err and err.name or "unknown reason"); - return true; -end); - -module:hook("stanza/http://jabber.org/protocol/compress:compress", function(event) - local session, stanza = event.origin, event.stanza; - - if session.type == "c2s" or session.type == "s2sin" then - -- fail if we are already compressed - if session.compressed then - local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed"); - (session.sends2s or session.send)(error_st); - session.log("debug", "Client tried to establish another compression layer."); - return true; - end - - -- checking if the compression method is supported - local method = stanza:get_child_text("method"); - if method == "zlib" then - session.log("debug", "zlib compression enabled."); - - -- create deflate and inflate streams - local deflate_stream = get_deflate_stream(session); - if not deflate_stream then return true; end - - local inflate_stream = get_inflate_stream(session); - if not inflate_stream then return true; end - - (session.sends2s or session.send)(st.stanza("compressed", {xmlns=xmlns_compression_protocol})); - session:reset_stream(); - - -- setup compression for session.w - setup_compression(session, deflate_stream); - - -- setup decompression for session.data - setup_decompression(session, inflate_stream); - - session.compressed = true; - elseif method then - session.log("debug", "%s compression selected, but we don't support it.", tostring(method)); - local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("unsupported-method"); - (session.sends2s or session.send)(error_st); - else - (session.sends2s or session.send)(st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed")); - end - return true; - end -end); -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_compression_unsafe/README.markdown Tue Sep 26 14:08:39 2017 +0100 @@ -0,0 +1,57 @@ +**NOTE:** XMPP compression has unresolved [security concerns](https://mail.jabber.org/pipermail/standards/2014-October/029215.html), +and this module has been removed from Prosody and renamed. + +While the bandwidth usage of XMPP isn't that much, compressing the data +sent to/from your server can give significant benefits to those on slow +connections, such as dial-up or mobile networks. Prosody supports +compression for client-to-server (if your client supports it) and +server-to-server streams using the mod\_compression plugin. + +# Details + +mod\_compression implements [XEP-0138], and supports the zlib compression +algorithm. + +## Dependencies + +The XMPP protocol specifies that all clients and servers supporting +compression must support the "zlib" compression method, and this is what +Prosody uses. However you will need to install zlib support for Lua on +your system. There are different ways of doing this depending on your +system. If in doubt whether it is installed correctly, the command +`lua -lzlib` in a console should open a Lua prompt with no errors. + +Debian/Ubuntu +: `apt-get install lua-zlib` + +LuaRocks +: `luarocks install lua-zlib` + +Source +: <https://github.com/brimworks/lua-zlib> + +# Usage + +``` lua +modules_enabled = { + -- Other modules + "compression"; -- Enable mod_compression +} +``` + +## Configuration + +The compression level can be set using the `compression_level` option +which can be a number from 1 to 9. Higher compression levels will use +more resources but less bandwidth. + +## Example + +``` lua +modules_enabled = { + -- Other modules + "compression"; -- Enable mod_compression +} + +compression_level = 5 +```
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_compression_unsafe/mod_compression_unsafe.lua Tue Sep 26 14:08:39 2017 +0100 @@ -0,0 +1,198 @@ +-- Prosody IM +-- Copyright (C) 2009-2012 Tobias Markmann +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local st = require "util.stanza"; +local zlib = require "zlib"; +local pcall = pcall; +local tostring = tostring; + +local xmlns_compression_feature = "http://jabber.org/features/compress" +local xmlns_compression_protocol = "http://jabber.org/protocol/compress" +local xmlns_stream = "http://etherx.jabber.org/streams"; +local compression_stream_feature = st.stanza("compression", {xmlns=xmlns_compression_feature}):tag("method"):text("zlib"):up(); +local add_filter = require "util.filters".add_filter; + +local compression_level = module:get_option_number("compression_level", 7); + +if not compression_level or compression_level < 1 or compression_level > 9 then + module:log("warn", "Invalid compression level in config: %s", tostring(compression_level)); + module:log("warn", "Module loading aborted. Compression won't be available."); + return; +end + +module:hook("stream-features", function(event) + local origin, features = event.origin, event.features; + if not origin.compressed and origin.type == "c2s" then + features:add_child(compression_stream_feature); + end +end); + +module:hook("s2s-stream-features", function(event) + local origin, features = event.origin, event.features; + if not origin.compressed and origin.type == "s2sin" then + features:add_child(compression_stream_feature); + end +end); + +-- Hook to activate compression if remote server supports it. +module:hook_stanza(xmlns_stream, "features", + function (session, stanza) + if not session.compressed and session.type == "s2sout" then + -- does remote server support compression? + local comp_st = stanza:get_child("compression", xmlns_compression_feature); + if comp_st then + -- do we support the mechanism + for a in comp_st:childtags("method") do + local algorithm = a:get_text(); + if algorithm == "zlib" then + session.sends2s(st.stanza("compress", {xmlns=xmlns_compression_protocol}):tag("method"):text("zlib")) + session.log("debug", "Enabled compression using zlib.") + return true; + end + end + session.log("debug", "Remote server supports no compression algorithm we support.") + end + end + end +, 250); + + +-- returns either nil or a fully functional ready to use inflate stream +local function get_deflate_stream(session) + local status, deflate_stream = pcall(zlib.deflate, compression_level); + if status == false then + local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed"); + (session.sends2s or session.send)(error_st); + session.log("error", "Failed to create zlib.deflate filter."); + module:log("error", "%s", tostring(deflate_stream)); + return + end + return deflate_stream +end + +-- returns either nil or a fully functional ready to use inflate stream +local function get_inflate_stream(session) + local status, inflate_stream = pcall(zlib.inflate); + if status == false then + local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed"); + (session.sends2s or session.send)(error_st); + session.log("error", "Failed to create zlib.inflate filter."); + module:log("error", "%s", tostring(inflate_stream)); + return + end + return inflate_stream +end + +-- setup compression for a stream +local function setup_compression(session, deflate_stream) + add_filter(session, "bytes/out", function(t) + local status, compressed, eof = pcall(deflate_stream, tostring(t), 'sync'); + if status == false then + module:log("warn", "%s", tostring(compressed)); + session:close({ + condition = "undefined-condition"; + text = compressed; + extra = st.stanza("failure", {xmlns="http://jabber.org/protocol/compress"}):tag("processing-failed"); + }); + return; + end + return compressed; + end); +end + +-- setup decompression for a stream +local function setup_decompression(session, inflate_stream) + add_filter(session, "bytes/in", function(data) + local status, decompressed, eof = pcall(inflate_stream, data); + if status == false then + module:log("warn", "%s", tostring(decompressed)); + session:close({ + condition = "undefined-condition"; + text = decompressed; + extra = st.stanza("failure", {xmlns="http://jabber.org/protocol/compress"}):tag("processing-failed"); + }); + return; + end + return decompressed; + end); +end + +module:hook("stanza/http://jabber.org/protocol/compress:compressed", function(event) + local session = event.origin; + + if session.type == "s2sout" then + session.log("debug", "Activating compression...") + -- create deflate and inflate streams + local deflate_stream = get_deflate_stream(session); + if not deflate_stream then return true; end + + local inflate_stream = get_inflate_stream(session); + if not inflate_stream then return true; end + + -- setup compression for session.w + setup_compression(session, deflate_stream); + + -- setup decompression for session.data + setup_decompression(session, inflate_stream); + session:reset_stream(); + session:open_stream(session.from_host, session.to_host); + session.compressed = true; + return true; + end +end); + +module:hook("stanza/http://jabber.org/protocol/compress:failure", function(event) + local err = event.stanza:get_child(); + (event.origin.log or module._log)("warn", "Compression setup failed (%s)", err and err.name or "unknown reason"); + return true; +end); + +module:hook("stanza/http://jabber.org/protocol/compress:compress", function(event) + local session, stanza = event.origin, event.stanza; + + if session.type == "c2s" or session.type == "s2sin" then + -- fail if we are already compressed + if session.compressed then + local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed"); + (session.sends2s or session.send)(error_st); + session.log("debug", "Client tried to establish another compression layer."); + return true; + end + + -- checking if the compression method is supported + local method = stanza:get_child_text("method"); + if method == "zlib" then + session.log("debug", "zlib compression enabled."); + + -- create deflate and inflate streams + local deflate_stream = get_deflate_stream(session); + if not deflate_stream then return true; end + + local inflate_stream = get_inflate_stream(session); + if not inflate_stream then return true; end + + (session.sends2s or session.send)(st.stanza("compressed", {xmlns=xmlns_compression_protocol})); + session:reset_stream(); + + -- setup compression for session.w + setup_compression(session, deflate_stream); + + -- setup decompression for session.data + setup_decompression(session, inflate_stream); + + session.compressed = true; + elseif method then + session.log("debug", "%s compression selected, but we don't support it.", tostring(method)); + local error_st = st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("unsupported-method"); + (session.sends2s or session.send)(error_st); + else + (session.sends2s or session.send)(st.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed")); + end + return true; + end +end); +