diff options
-rwxr-xr-x | configure | 5 | ||||
-rw-r--r-- | core/hostmanager.lua | 12 | ||||
-rw-r--r-- | core/loggingmanager.lua | 2 | ||||
-rw-r--r-- | core/modulemanager.lua | 2 | ||||
-rw-r--r-- | core/s2smanager.lua | 178 | ||||
-rw-r--r-- | core/sessionmanager.lua | 1 | ||||
-rw-r--r-- | core/usermanager.lua | 6 | ||||
-rw-r--r-- | net/dns.lua | 1 | ||||
-rw-r--r-- | net/httpserver.lua | 1 | ||||
-rw-r--r-- | net/server_event.lua | 9 | ||||
-rw-r--r-- | net/server_select.lua | 22 | ||||
-rw-r--r-- | net/xmppcomponent_listener.lua | 2 | ||||
-rw-r--r-- | net/xmppserver_listener.lua | 4 | ||||
-rw-r--r-- | plugins/adhoc/mod_adhoc.lua | 16 | ||||
-rw-r--r-- | plugins/mod_bosh.lua | 54 | ||||
-rw-r--r-- | plugins/mod_component.lua | 4 | ||||
-rw-r--r-- | plugins/mod_tls.lua | 2 | ||||
-rw-r--r-- | tests/test.lua | 1 | ||||
-rw-r--r-- | tests/test_util_rfc3484.lua | 51 | ||||
-rw-r--r-- | tools/migration/migrator/jabberd14.lua | 15 | ||||
-rw-r--r-- | util/array.lua | 49 | ||||
-rw-r--r-- | util/dataforms.lua | 22 | ||||
-rw-r--r-- | util/datamanager.lua | 6 | ||||
-rw-r--r-- | util/debug.lua | 1 | ||||
-rw-r--r-- | util/dependencies.lua | 8 | ||||
-rw-r--r-- | util/ip.lua | 176 | ||||
-rw-r--r-- | util/iterators.lua | 2 | ||||
-rw-r--r-- | util/rfc3484.lua | 133 | ||||
-rw-r--r-- | util/stanza.lua | 5 | ||||
-rw-r--r-- | util/xmppstream.lua | 15 |
30 files changed, 656 insertions, 149 deletions
@@ -112,7 +112,7 @@ do CFLAGS="-Wall -fPIC" LDFLAGS="-shared" fi - if [ "$OSTYPE" = "freebsd" ] + if [ "$OSTYPE" = "freebsd" -o "$OSTYPE" = "openbsd" ] then LUA_INCDIR="/usr/local/include/lua51" LUA_INCDIR_SET=yes CFLAGS="-Wall -fPIC -I/usr/local/include" @@ -122,6 +122,9 @@ do LUA_DIR=/usr/local LUA_DIR_SET=yes fi + if [ "$OSTYPE" = "openbsd" ] + then LUA_INCDIR="/usr/local/include"; + fi ;; --datadir=*) DATADIR="$value" diff --git a/core/hostmanager.lua b/core/hostmanager.lua index 9e74cd6b..0dd1d426 100644 --- a/core/hostmanager.lua +++ b/core/hostmanager.lua @@ -53,6 +53,17 @@ end prosody_events.add_handler("server-starting", load_enabled_hosts); +local function host_send(stanza) + local name, type = stanza.name, stanza.attr.type; + if type == "error" or (name == "iq" and type == "result") then + local dest_host_name = select(2, jid_split(stanza.attr.to)); + local dest_host = hosts[dest_host_name] or { type = "unknown" }; + log("warn", "Unhandled response sent to %s host %s: %s", dest_host.type, dest_host_name, tostring(stanza)); + return; + end + core_route_stanza(nil, stanza); +end + function activate(host, host_config) if hosts[host] then return nil, "The host "..host.." is already activated"; end host_config = host_config or configmanager.getconfig()[host]; @@ -63,6 +74,7 @@ function activate(host, host_config) events = events_new(); dialback_secret = configmanager.get(host, "core", "dialback_secret") or uuid_gen(); disallow_s2s = configmanager.get(host, "core", "disallow_s2s"); + send = host_send; }; if not host_config.core.component_module then -- host host_session.type = "local"; diff --git a/core/loggingmanager.lua b/core/loggingmanager.lua index 88f2bbbf..426425c1 100644 --- a/core/loggingmanager.lua +++ b/core/loggingmanager.lua @@ -41,7 +41,7 @@ local logging_config; local apply_sink_rules; local log_sink_types = setmetatable({}, { __newindex = function (t, k, v) rawset(t, k, v); apply_sink_rules(k); end; }); local get_levels; -local logging_levels = { "debug", "info", "warn", "error", "critical" } +local logging_levels = { "debug", "info", "warn", "error" } -- Put a rule into action. Requires that the sink type has already been registered. -- This function is called automatically when a new sink type is added [see apply_sink_rules()] diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 2d1eeb77..c4d95695 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -117,7 +117,7 @@ function load(host, module_name, config) end local _log = logger.init(host..":"..module_name); - local api_instance = setmetatable({ name = module_name, host = host, path = err, config = config, _log = _log, log = function (self, ...) return _log(...); end }, { __index = api }); + local api_instance = setmetatable({ name = module_name, host = host, path = err, _log = _log, log = function (self, ...) return _log(...); end }, { __index = api }); local pluginenv = setmetatable({ module = api_instance }, { __index = _G }); api_instance.environment = pluginenv; diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 974b2f4f..f44921c3 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -16,18 +16,19 @@ local socket = require "socket"; local format = string.format; local t_insert, t_sort = table.insert, table.sort; local get_traceback = debug.traceback; -local tostring, pairs, ipairs, getmetatable, newproxy, error, tonumber, setmetatable - = tostring, pairs, ipairs, getmetatable, newproxy, error, tonumber, setmetatable; +local tostring, pairs, ipairs, getmetatable, newproxy, type, error, tonumber, setmetatable + = tostring, pairs, ipairs, getmetatable, newproxy, type, error, tonumber, setmetatable; local idna_to_ascii = require "util.encodings".idna.to_ascii; local connlisteners_get = require "net.connlisteners".get; local initialize_filters = require "util.filters".initialize; local wrapclient = require "net.server".wrapclient; -local modulemanager = require "core.modulemanager"; local st = require "stanza"; local stanza = st.stanza; local nameprep = require "util.encodings".stringprep.nameprep; local cert_verify_identity = require "util.x509".verify_identity; +local new_ip = require "util.ip".new_ip; +local rfc3484_dest = require "util.rfc3484".destination; local fire_event = prosody.events.fire_event; local uuid_gen = require "util.uuid".generate; @@ -43,6 +44,7 @@ local config = require "core.configmanager"; local connect_timeout = config.get("*", "core", "s2s_timeout") or 60; local dns_timeout = config.get("*", "core", "dns_timeout") or 15; local max_dns_depth = config.get("*", "core", "dns_max_depth") or 3; +local sources; dns.settimeout(dns_timeout); @@ -243,6 +245,11 @@ function attempt_connection(host_session, err) for _, record in ipairs(answer) do t_insert(srv_hosts, record.srv); end + if #srv_hosts == 1 and srv_hosts[1].target == "." then + log("debug", to_host.." does not provide a XMPP service"); + destroy_session(host_session, err); -- Nothing to see here + return; + end t_sort(srv_hosts, compare_srv_priorities); local srv_choice = srv_hosts[1]; @@ -265,6 +272,8 @@ function attempt_connection(host_session, err) end, "_xmpp-server._tcp."..connect_host..".", "SRV"); return true; -- Attempt in progress + elseif host_session.ip_hosts then + return try_connect(host_session, connect_host, connect_port, err); elseif host_session.srv_hosts and #host_session.srv_hosts > host_session.srv_choice then -- Not our first attempt, and we also have SRV host_session.srv_choice = host_session.srv_choice + 1; local srv_choice = host_session.srv_hosts[host_session.srv_choice]; @@ -285,54 +294,147 @@ function attempt_connection(host_session, err) return try_connect(host_session, connect_host, connect_port); end -function try_connect(host_session, connect_host, connect_port) +function try_next_ip(host_session) + host_session.connecting = nil; + host_session.ip_choice = host_session.ip_choice + 1; + local ip = host_session.ip_hosts[host_session.ip_choice]; + local ok, err= make_connect(host_session, ip.ip, ip.port); + if not ok then + if not attempt_connection(host_session, err or "closed") then + err = err and (": "..err) or ""; + destroy_session(host_session, "Connection failed"..err); + end + end +end + +function try_connect(host_session, connect_host, connect_port, err) host_session.connecting = true; - local handle; - handle = adns.lookup(function (reply, err) - handle = nil; - host_session.connecting = nil; - - -- COMPAT: This is a compromise for all you CNAME-(ab)users :) - if not (reply and reply[#reply] and reply[#reply].a) then - local count = max_dns_depth; - reply = dns.peek(connect_host, "CNAME", "IN"); - while count > 0 and reply and reply[#reply] and not reply[#reply].a and reply[#reply].cname do - log("debug", "Looking up %s (DNS depth is %d)", tostring(reply[#reply].cname), count); - reply = dns.peek(reply[#reply].cname, "A", "IN") or dns.peek(reply[#reply].cname, "CNAME", "IN"); - count = count - 1; + + if not err then + local IPs = {}; + host_session.ip_hosts = IPs; + local handle4, handle6; + local has_other = false; + + if not sources then + sources = {}; + local cfg_sources = config.get("*", "core", "interface") or connlisteners_get("xmppserver").default_interface; + if type(cfg_sources) == "string" then + cfg_sources = { cfg_sources }; + end + for i, source in ipairs(cfg_sources) do + if source == "*" then + sources[i] = new_ip("0.0.0.0", "IPv4"); + else + sources[i] = new_ip(source, (source:find(":") and "IPv6") or "IPv4"); + end end end - -- end of CNAME resolving - - if reply and reply[#reply] and reply[#reply].a then - log("debug", "DNS reply for %s gives us %s", connect_host, reply[#reply].a); - local ok, err = make_connect(host_session, reply[#reply].a, connect_port); - if not ok then - if not attempt_connection(host_session, err or "closed") then - err = err and (": "..err) or ""; - destroy_session(host_session, "Connection failed"..err); + + handle4 = adns.lookup(function (reply, err) + handle4 = nil; + + -- COMPAT: This is a compromise for all you CNAME-(ab)users :) + if not (reply and reply[#reply] and reply[#reply].a) then + local count = max_dns_depth; + reply = dns.peek(connect_host, "CNAME", "IN"); + while count > 0 and reply and reply[#reply] and not reply[#reply].a and reply[#reply].cname do + log("debug", "Looking up %s (DNS depth is %d)", tostring(reply[#reply].cname), count); + reply = dns.peek(reply[#reply].cname, "A", "IN") or dns.peek(reply[#reply].cname, "CNAME", "IN"); + count = count - 1; end end - else - 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); - err = err and (": "..err) or ""; - destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't + -- end of CNAME resolving + + if reply and reply[#reply] and reply[#reply].a then + for _, ip in ipairs(reply) do + log("debug", "DNS reply for %s gives us %s", connect_host, ip.a); + IPs[#IPs+1] = new_ip(ip.a, "IPv4"); + end + end + + if has_other then + if #IPs > 0 then + rfc3484_dest(host_session.ip_hosts, sources); + for i = 1, #IPs do + IPs[i] = {ip = IPs[i], port = connect_port}; + end + host_session.ip_choice = 0; + try_next_ip(host_session); + else + log("debug", "DNS lookup failed to get a response for %s", connect_host); + host_session.ip_hosts = nil; + 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); + err = err and (": "..err) or ""; + destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't + end + end + else + has_other = true; + end + end, connect_host, "A", "IN"); + + handle6 = adns.lookup(function (reply, err) + handle6 = nil; + + if reply and reply[#reply] and reply[#reply].aaaa then + for _, ip in ipairs(reply) do + log("debug", "DNS reply for %s gives us %s", connect_host, ip.aaaa); + IPs[#IPs+1] = new_ip(ip.aaaa, "IPv6"); + end + end + + if has_other then + if #IPs > 0 then + rfc3484_dest(host_session.ip_hosts, sources); + for i = 1, #IPs do + IPs[i] = {ip = IPs[i], port = connect_port}; + end + host_session.ip_choice = 0; + try_next_ip(host_session); + else + log("debug", "DNS lookup failed to get a response for %s", connect_host); + host_session.ip_hosts = nil; + 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); + err = err and (": "..err) or ""; + destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't + end + end + else + has_other = true; end + end, connect_host, "AAAA", "IN"); + + return true; + elseif host_session.ip_hosts and #host_session.ip_hosts > host_session.ip_choice then -- Not our first attempt, and we also have IPs left to try + try_next_ip(host_session); + else + host_session.ip_hosts = nil; + if not attempt_connection(host_session, "out of IP addresses") then -- Retry if we can + log("debug", "No other records to try for %s - destroying", host_session.to_host); + err = err and (": "..err) or ""; + destroy_session(host_session, "Connecting failed"..err); -- End of the line, we can't + return false; end - end, connect_host, "A", "IN"); + end return true; end function make_connect(host_session, connect_host, connect_port) - (host_session.log or log)("info", "Beginning new connection attempt to %s (%s:%d)", host_session.to_host, connect_host, connect_port); + (host_session.log or log)("info", "Beginning new connection attempt to %s ([%s]:%d)", host_session.to_host, connect_host.addr, connect_port); -- Ok, we're going to try to connect local from_host, to_host = host_session.from_host, host_session.to_host; - local conn, handler = socket.tcp(); + local conn, handler; + if connect_host.proto == "IPv4" then + conn, handler = socket.tcp(); + else + conn, handler = socket.tcp6(); + end if not conn then log("warn", "Failed to create outgoing connection, system error: %s", handler); @@ -340,14 +442,14 @@ function make_connect(host_session, connect_host, connect_port) end conn:settimeout(0); - local success, err = conn:connect(connect_host, connect_port); + local success, err = conn:connect(connect_host.addr, connect_port); if not success and err ~= "timeout" then - log("warn", "s2s connect() to %s (%s:%d) failed: %s", host_session.to_host, connect_host, connect_port, err); + log("warn", "s2s connect() to %s (%s:%d) failed: %s", host_session.to_host, connect_host.addr, connect_port, err); return false, err; end local cl = connlisteners_get("xmppserver"); - conn = wrapclient(conn, connect_host, connect_port, cl, cl.default_mode or 1 ); + conn = wrapclient(conn, connect_host.addr, connect_port, cl, cl.default_mode or 1 ); host_session.conn = conn; local filter = initialize_filters(host_session); diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index 1de6c41a..b1ec819f 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -16,7 +16,6 @@ local hosts = hosts; local full_sessions = full_sessions; local bare_sessions = bare_sessions; -local modulemanager = require "core.modulemanager"; local logger = require "util.logger"; local log = logger.init("sessionmanager"); local error = error; diff --git a/core/usermanager.lua b/core/usermanager.lua index 0152afd7..9e5a016c 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -11,6 +11,7 @@ local log = require "util.logger".init("usermanager"); local type = type; local ipairs = ipairs; local jid_bare = require "util.jid".bare; +local jid_prep = require "util.jid".prep; local config = require "core.configmanager"; local hosts = hosts; local sasl_new = require "util.sasl".new; @@ -97,6 +98,7 @@ end function is_admin(jid, host) if host and not hosts[host] then return false; end + if type(jid) ~= "string" then return false; end local is_admin; jid = jid_bare(jid); @@ -108,7 +110,7 @@ function is_admin(jid, host) if host_admins and host_admins ~= global_admins then if type(host_admins) == "table" then for _,admin in ipairs(host_admins) do - if admin == jid then + if jid_prep(admin) == jid then is_admin = true; break; end @@ -121,7 +123,7 @@ function is_admin(jid, host) if not is_admin and global_admins then if type(global_admins) == "table" then for _,admin in ipairs(global_admins) do - if admin == jid then + if jid_prep(admin) == jid then is_admin = true; break; end diff --git a/net/dns.lua b/net/dns.lua index 8f428476..5b32a56c 100644 --- a/net/dns.lua +++ b/net/dns.lua @@ -358,6 +358,7 @@ function resolver:name() -- - - - - - - - - - - - - - - - - - - - - - name local remember, pointers = nil, 0; local len = self:byte(); local n = {}; + if len == 0 then return "." end -- Root label while len > 0 do if len >= 0xc0 then -- name is "compressed" pointers = pointers + 1; diff --git a/net/httpserver.lua b/net/httpserver.lua index 74f61c56..44e8e24d 100644 --- a/net/httpserver.lua +++ b/net/httpserver.lua @@ -7,7 +7,6 @@ -- -local server = require "net.server" local url_parse = require "socket.url".parse; local httpstream_new = require "util.httpstream".new; diff --git a/net/server_event.lua b/net/server_event.lua index f4745c34..dbf5161f 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -295,7 +295,10 @@ do end function interface_mt:resume() - return self:_lock(self.nointerface, false, self.nowriting); + self:_lock(self.nointerface, false, self.nowriting); + if not self.eventread then + self.eventread = addevent( base, self.conn, EV_READ, self.readcallback, cfg.READ_TIMEOUT ); -- register callback + end end function interface_mt:counter(c) @@ -642,6 +645,10 @@ do return -1 end end + if interface.noreading then + interface.eventread = nil; + return -1; + end return EV_READ, cfg.READ_TIMEOUT end end diff --git a/net/server_select.lua b/net/server_select.lua index 412d03c0..8802f620 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -596,25 +596,23 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport handler.sendbuffer = handshake handshake( socket ) -- do handshake end - handler.readbuffer = _readbuffer - handler.sendbuffer = _sendbuffer - - if sslctx then - out_put "server.lua: auto-starting ssl negotiation..." - handler.autostart_ssl = true; - handler:starttls(sslctx); - end - - else - handler.readbuffer = _readbuffer - handler.sendbuffer = _sendbuffer end + + handler.readbuffer = _readbuffer + handler.sendbuffer = _sendbuffer send = socket.send receive = socket.receive shutdown = ( ssl and id ) or socket.shutdown _socketlist[ socket ] = handler _readlistlen = addsocket(_readlist, socket, _readlistlen) + + if sslctx and luasec then + out_put "server.lua: auto-starting ssl negotiation..." + handler.autostart_ssl = true; + handler:starttls(sslctx); + end + return handler, socket end diff --git a/net/xmppcomponent_listener.lua b/net/xmppcomponent_listener.lua index 90293559..dd7b2b91 100644 --- a/net/xmppcomponent_listener.lua +++ b/net/xmppcomponent_listener.lua @@ -7,8 +7,6 @@ -- -local hosts = _G.hosts; - local t_concat = table.concat; local tostring = tostring; local type = type; diff --git a/net/xmppserver_listener.lua b/net/xmppserver_listener.lua index 3af0b962..048c6c7d 100644 --- a/net/xmppserver_listener.lua +++ b/net/xmppserver_listener.lua @@ -66,7 +66,7 @@ function stream_callbacks.handlestanza(session, stanza) end local sessions = {}; -local xmppserver = { default_port = 5269, default_mode = "*a" }; +local xmppserver = { default_port = 5269, default_mode = "*a", default_interface = "*" }; -- These are session methods -- @@ -178,7 +178,7 @@ end function xmppserver.ondisconnect(conn, err) local session = sessions[conn]; if session then - if err and err ~= "closed" and session.srv_hosts then + if err and err ~= "closed" and session.type == "s2sout_unauthed" then (session.log or log)("debug", "s2s connection attempt failed: %s", err); if s2s_attempt_connect(session, err) then (session.log or log)("debug", "...so we're going to try another target"); diff --git a/plugins/adhoc/mod_adhoc.lua b/plugins/adhoc/mod_adhoc.lua index 6d826338..49d07103 100644 --- a/plugins/adhoc/mod_adhoc.lua +++ b/plugins/adhoc/mod_adhoc.lua @@ -90,19 +90,13 @@ module:hook("iq/host/"..xmlns_cmd..":command", function (event) end end, 500); -local function handle_item_added(item) +local function adhoc_added(event) + local item = event.item; commands[item.node] = item; end -module:hook("item-added/adhoc", function (event) - return handle_item_added(event.item); -end, 500); - -module:hook("item-removed/adhoc", function (event) +local function adhoc_removed(event) commands[event.item.node] = nil; -end, 500); - --- Pick up any items that are already added -for _, item in ipairs(module:get_host_items("adhoc")) do - handle_item_added(item); end + +module:handle_items("adhoc", adhoc_added, adhoc_removed); diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 834b128a..9de27b4b 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -9,7 +9,6 @@ module.host = "*" -- Global module local hosts = _G.hosts; -local lxp = require "lxp"; local new_xmpp_stream = require "util.xmppstream".new; local httpserver = require "net.httpserver"; local sm = require "core.sessionmanager"; @@ -35,6 +34,7 @@ local BOSH_DEFAULT_POLLING = module:get_option_number("bosh_max_polling", 5); local BOSH_DEFAULT_REQUESTS = module:get_option_number("bosh_max_requests", 2); local consider_bosh_secure = module:get_option_boolean("consider_bosh_secure"); +local auto_cork = module:get_option_boolean("bosh_auto_cork", false); local default_headers = { ["Content-Type"] = "text/xml; charset=utf-8" }; @@ -57,7 +57,7 @@ end local trusted_proxies = module:get_option_set("trusted_proxies", {"127.0.0.1"})._items; local function get_ip_from_request(request) - local ip = request.handler:ip(); + local ip = request.conn:ip(); local forwarded_for = request.headers["x-forwarded-for"]; if forwarded_for then forwarded_for = forwarded_for..", "..ip; @@ -91,9 +91,10 @@ function on_destroy_request(request) end -- If this session now has no requests open, mark it as inactive - if #requests == 0 and session.bosh_max_inactive and not inactive_sessions[session] then - inactive_sessions[session] = os_time(); - (session.log or log)("debug", "BOSH session marked as inactive at %d", inactive_sessions[session]); + local max_inactive = session.bosh_max_inactive; + if max_inactive and #requests == 0 then + inactive_sessions[session] = os_time() + max_inactive; + (session.log or log)("debug", "BOSH session marked as inactive (for %ds)", max_inactive); end end end @@ -119,10 +120,17 @@ function handle_request(method, body, request) request.on_destroy = on_destroy_request; local stream = new_xmpp_stream(request, stream_callbacks); + -- stream:feed() calls the stream_callbacks, so all stanzas in -- the body are processed in this next line before it returns. + -- 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. stream:feed(body); + -- Stanzas (if any) in the request have now been processed, and + -- we take care of the high-level BOSH logic here, including + -- giving a response or putting the request "on hold". local session = sessions[request.sid]; if session then -- Session was marked as inactive, since we have @@ -213,9 +221,11 @@ local function bosh_close_stream(session, reason) held_request:destroy(); end sessions[session.sid] = nil; + inactive_sessions[session] = nil; sm_destroy_session(session); end +-- Handle the <body> tag in the request payload. function stream_callbacks.streamopened(request, attr) local sid = attr.sid; log("debug", "BOSH body open (sid: %s)", sid or "<none>"); @@ -258,7 +268,7 @@ function stream_callbacks.streamopened(request, attr) end --log("debug", "Sending BOSH data: %s", tostring(s)); local oldest_request = r[1]; - if oldest_request then + if oldest_request and (not(auto_cork) or waiting_requests[oldest_request]) then log("debug", "We have an open request, so sending on that"); response.body = t_concat({ "<body xmlns='http://jabber.org/protocol/httpbind' ", @@ -338,14 +348,6 @@ function stream_callbacks.streamopened(request, attr) session.rid = rid; end - if session.notopen then - local features = st.stanza("stream:features"); - hosts[session.host].events.fire_event("stream-features", { origin = session, features = features }); - fire_event("stream-features", session, features); - session.send(features); - session.notopen = nil; - end - if attr.type == "terminate" then -- Client wants to end this session, which we'll do -- after processing any stanzas in this request @@ -355,6 +357,14 @@ function stream_callbacks.streamopened(request, attr) request.notopen = nil; -- Signals that we accept this opening tag t_insert(session.requests, request); request.sid = sid; + + if session.notopen then + local features = st.stanza("stream:features"); + hosts[session.host].events.fire_event("stream-features", { origin = session, features = features }); + fire_event("stream-features", session, features); + session.send(features); + session.notopen = nil; + end end function stream_callbacks.handlestanza(request, stanza) @@ -402,17 +412,13 @@ function on_timer() now = now - 3; local n_dead_sessions = 0; - for session, inactive_since in pairs(inactive_sessions) do - if session.bosh_max_inactive then - if now - inactive_since > session.bosh_max_inactive then - (session.log or log)("debug", "BOSH client inactive too long, destroying session at %d", now); - sessions[session.sid] = nil; - inactive_sessions[session] = nil; - n_dead_sessions = n_dead_sessions + 1; - dead_sessions[n_dead_sessions] = session; - end - else + for session, close_after in pairs(inactive_sessions) do + if close_after < now then + (session.log or log)("debug", "BOSH client inactive too long, destroying session at %d", now); + sessions[session.sid] = nil; inactive_sessions[session] = nil; + n_dead_sessions = n_dead_sessions + 1; + dead_sessions[n_dead_sessions] = session; end end diff --git a/plugins/mod_component.lua b/plugins/mod_component.lua index 97c46a8c..f7d09930 100644 --- a/plugins/mod_component.lua +++ b/plugins/mod_component.lua @@ -10,8 +10,6 @@ if module:get_host_type() ~= "component" then error("Don't load mod_component manually, it should be for a component, please see http://prosody.im/doc/components", 0); end -local hosts = _G.hosts; - local t_concat = table.concat; local sha1 = require "util.hashes".sha1; @@ -23,6 +21,7 @@ local main_session, send; local function on_destroy(session, err) if main_session == session then + connected = false; main_session = nil; send = nil; session.on_destroy = nil; @@ -83,6 +82,7 @@ function handle_component_auth(event) -- If component not already created for this host, create one now if not main_session then + connected = true; send = session.send; main_session = session; session.on_destroy = on_destroy; diff --git a/plugins/mod_tls.lua b/plugins/mod_tls.lua index cace2d69..707ae8f5 100644 --- a/plugins/mod_tls.lua +++ b/plugins/mod_tls.lua @@ -75,7 +75,7 @@ end); module:hook_stanza("http://etherx.jabber.org/streams", "features", function (session, stanza) module:log("debug", "Received features element"); if can_do_tls(session) and stanza:child_with_ns(xmlns_starttls) then - module:log("%s is offering TLS, taking up the offer...", session.to_host); + module:log("debug", "%s is offering TLS, taking up the offer...", session.to_host); session.sends2s("<starttls xmlns='"..xmlns_starttls.."'/>"); return true; end diff --git a/tests/test.lua b/tests/test.lua index 000c3ee9..db727ce1 100644 --- a/tests/test.lua +++ b/tests/test.lua @@ -12,6 +12,7 @@ function run_all_tests() package.loaded["net.connlisteners"] = { get = function () return {} end }; dotest "util.jid" dotest "util.multitable" + dotest "util.rfc3484" dotest "net.http" dotest "core.modulemanager" dotest "core.stanza_router" diff --git a/tests/test_util_rfc3484.lua b/tests/test_util_rfc3484.lua new file mode 100644 index 00000000..18ae310e --- /dev/null +++ b/tests/test_util_rfc3484.lua @@ -0,0 +1,51 @@ +-- Prosody IM +-- Copyright (C) 2011 Florian Zeitz +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +function source(source) + local new_ip = require"util.ip".new_ip; + assert_equal(source(new_ip("2001::1", "IPv6"), {new_ip("3ffe::1", "IPv6"), new_ip("fe80::1", "IPv6")}).addr, "3ffe::1", "prefer appropriate scope"); + assert_equal(source(new_ip("2001::1", "IPv6"), {new_ip("fe80::1", "IPv6"), new_ip("fec0::1", "IPv6")}).addr, "fec0::1", "prefer appropriate scope"); + assert_equal(source(new_ip("fec0::1", "IPv6"), {new_ip("fe80::1", "IPv6"), new_ip("2001::1", "IPv6")}).addr, "2001::1", "prefer appropriate scope"); + assert_equal(source(new_ip("ff05::1", "IPv6"), {new_ip("fe80::1", "IPv6"), new_ip("fec0::1", "IPv6"), new_ip("2001::1", "IPv6")}).addr, "fec0::1", "prefer appropriate scope"); + assert_equal(source(new_ip("2001::1", "IPv6"), {new_ip("2001::1", "IPv6"), new_ip("2002::1", "IPv6")}).addr, "2001::1", "prefer same address"); + assert_equal(source(new_ip("fec0::1", "IPv6"), {new_ip("fec0::2", "IPv6"), new_ip("2001::1", "IPv6")}).addr, "fec0::2", "prefer appropriate scope"); + assert_equal(source(new_ip("2001::1", "IPv6"), {new_ip("2001::2", "IPv6"), new_ip("3ffe::2", "IPv6")}).addr, "2001::2", "longest matching prefix"); + assert_equal(source(new_ip("2002:836b:2179::1", "IPv6"), {new_ip("2002:836b:2179::d5e3:7953:13eb:22e8", "IPv6"), new_ip("2001::2", "IPv6")}).addr, "2002:836b:2179::d5e3:7953:13eb:22e8", "prefer matching label"); +end + +function destination(dest) + local order; + local new_ip = require"util.ip".new_ip; + order = dest({new_ip("2001::1", "IPv6"), new_ip("131.107.65.121", "IPv4")}, {new_ip("2001::2", "IPv6"), new_ip("fe80::1", "IPv6"), new_ip("169.254.13.78", "IPv4")}) + assert_equal(order[1].addr, "2001::1", "prefer matching scope"); + assert_equal(order[2].addr, "131.107.65.121", "prefer matching scope") + + order = dest({new_ip("2001::1", "IPv6"), new_ip("131.107.65.121", "IPv4")}, {new_ip("fe80::1", "IPv6"), new_ip("131.107.65.117", "IPv4")}) + assert_equal(order[1].addr, "131.107.65.121", "prefer matching scope") + assert_equal(order[2].addr, "2001::1", "prefer matching scope") + + order = dest({new_ip("2001::1", "IPv6"), new_ip("10.1.2.3", "IPv4")}, {new_ip("2001::2", "IPv6"), new_ip("fe80::1", "IPv6"), new_ip("10.1.2.4", "IPv4")}) + assert_equal(order[1].addr, "2001::1", "prefer higher precedence"); + assert_equal(order[2].addr, "10.1.2.3", "prefer higher precedence"); + + order = dest({new_ip("2001::1", "IPv6"), new_ip("fec0::1", "IPv6"), new_ip("fe80::1", "IPv6")}, {new_ip("2001::2", "IPv6"), new_ip("fec0::1", "IPv6"), new_ip("fe80::2", "IPv6")}) + assert_equal(order[1].addr, "fe80::1", "prefer smaller scope"); + assert_equal(order[2].addr, "fec0::1", "prefer smaller scope"); + assert_equal(order[3].addr, "2001::1", "prefer smaller scope"); + + order = dest({new_ip("2001::1", "IPv6"), new_ip("3ffe::1", "IPv6")}, {new_ip("2001::2", "IPv6"), new_ip("3f44::2", "IPv6"), new_ip("fe80::2", "IPv6")}) + assert_equal(order[1].addr, "2001::1", "longest matching prefix"); + assert_equal(order[2].addr, "3ffe::1", "longest matching prefix"); + + order = dest({new_ip("2002:836b:4179::1", "IPv6"), new_ip("2001::1", "IPv6")}, {new_ip("2002:836b:4179::2", "IPv6"), new_ip("fe80::2", "IPv6")}) + assert_equal(order[1].addr, "2002:836b:4179::1", "prefer matching label"); + assert_equal(order[2].addr, "2001::1", "prefer matching label"); + + order = dest({new_ip("2002:836b:4179::1", "IPv6"), new_ip("2001::1", "IPv6")}, {new_ip("2002:836b:4179::2", "IPv6"), new_ip("2001::2", "IPv6"), new_ip("fe80::2", "IPv6")}) + assert_equal(order[1].addr, "2001::1", "prefer higher precedence"); + assert_equal(order[2].addr, "2002:836b:4179::1", "prefer higher precedence"); +end diff --git a/tools/migration/migrator/jabberd14.lua b/tools/migration/migrator/jabberd14.lua index 47004a52..54c45d94 100644 --- a/tools/migration/migrator/jabberd14.lua +++ b/tools/migration/migrator/jabberd14.lua @@ -67,15 +67,12 @@ local parse_xml = (function() end)(); local function load_xml(path) - if path then - local f, err = io_open(path); - if not f then return f, err; end - local data = f:read("*a"); - f:close(); - if data then - return parse_xml(data); - end - end + local f, err = io_open(path); + if not f then return f, err; end + local data = f:read("*a"); + f:close(); + if not data then return; end + return parse_xml(data); end local function load_spool_file(host, filename, path) diff --git a/util/array.lua b/util/array.lua index 5dbd3037..5dc604ba 100644 --- a/util/array.lua +++ b/util/array.lua @@ -9,6 +9,11 @@ local t_insert, t_sort, t_remove, t_concat = table.insert, table.sort, table.remove, table.concat; +local setmetatable = setmetatable; +local math_random = math.random; +local pairs, ipairs = pairs, ipairs; +local tostring = tostring; + local array = {}; local array_base = {}; local array_methods = {}; @@ -25,6 +30,15 @@ end setmetatable(array, { __call = new_array }); +-- Read-only methods +function array_methods:random() + return self[math_random(1,#self)]; +end + +-- These methods can be called two ways: +-- array.method(existing_array, [params [, ...]]) -- Create new array for result +-- existing_array:method([params, ...]) -- Transform existing array into result +-- function array_base.map(outa, ina, func) for k,v in ipairs(ina) do outa[k] = func(v); @@ -60,15 +74,18 @@ function array_base.sort(outa, ina, ...) return outa; end ---- These methods only mutate -function array_methods:random() - return self[math.random(1,#self)]; +function array_base.pluck(outa, ina, key) + for i=1,#ina do + outa[i] = ina[i][key]; + end + return outa; end +--- These methods only mutate the array function array_methods:shuffle(outa, ina) local len = #self; for i=1,#self do - local r = math.random(i,len); + local r = math_random(i,len); self[i], self[r] = self[r], self[i]; end return self; @@ -91,10 +108,24 @@ function array_methods:append(array) return self; end -array_methods.push = table.insert; -array_methods.pop = table.remove; -array_methods.concat = table.concat; -array_methods.length = function (t) return #t; end +function array_methods:push(x) + t_insert(self, x); + return self; +end + +function array_methods:pop(x) + local v = self[x]; + t_remove(self, x); + return v; +end + +function array_methods:concat(sep) + return t_concat(array.map(self, tostring), sep); +end + +function array_methods:length() + return #self; +end --- These methods always create a new array function array.collect(f, s, var) @@ -102,7 +133,7 @@ function array.collect(f, s, var) while true do var = f(s, var); if var == nil then break; end - table.insert(t, var); + t_insert(t, var); end return setmetatable(t, array_mt); end diff --git a/util/dataforms.lua b/util/dataforms.lua index e4d24cf6..d4a1865c 100644 --- a/util/dataforms.lua +++ b/util/dataforms.lua @@ -120,12 +120,18 @@ function form_t.data(layout, stanza) end end - local reader = field_readers[field.type]; - local verifier = field.verifier or field_verifiers[field.type]; - if reader then - data[field.name] = reader(tag); - if verifier then - errors[field.name] = verifier(data[field.name], tag, field.required); + if not tag then + if field.required then + errors[field.name] = "Required value missing"; + end + else + local reader = field_readers[field.type]; + local verifier = field.verifier or field_verifiers[field.type]; + if reader then + data[field.name] = reader(tag); + if verifier then + errors[field.name] = verifier(data[field.name], tag, field.required); + end end end end @@ -161,7 +167,7 @@ field_readers["jid-single"] = field_verifiers["jid-single"] = function (data, field_tag, required) - if #data == 0 and required then + if ((not data) or (#data == 0)) and required then return "Required value missing"; end if not jid_prep(data) then @@ -246,7 +252,7 @@ field_readers["boolean"] = field_verifiers["boolean"] = function (data, field_tag, required) data = field_readers["text-single"](field_tag); - if #data == 0 and required then + if ((not data) or (#data == 0)) and required then return "Required value missing"; end if data ~= "1" and data ~= "true" and data ~= "0" and data ~= "false" then diff --git a/util/datamanager.lua b/util/datamanager.lua index d5e9c88c..a5d676cc 100644 --- a/util/datamanager.lua +++ b/util/datamanager.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. -- @@ -20,7 +20,7 @@ local error = error; local next = next; local t_insert = table.insert; local append = require "util.serialization".append; -local path_separator = "/"; if os.getenv("WINDIR") then path_separator = "\\" end +local path_separator = assert ( package.config:match ( "^([^\n]+)" ) , "package.config not in standard form" ) -- Extract directory seperator from package.config (an undocumented string that comes with lua) local lfs = require "lfs"; local prosody = prosody; local raw_mkdir; @@ -72,7 +72,7 @@ local function callback(username, host, datastore, data) username, host, datastore, data = f(username, host, datastore, data); if username == false then break; end end - + return username, host, datastore, data; end function add_callback(func) diff --git a/util/debug.lua b/util/debug.lua index 22d02bf2..2170a6d1 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -9,6 +9,7 @@ local censored_names = { }; local function get_locals_table(level) + level = level + 1; -- Skip this function itself local locals = {}; for local_num = 1, math.huge do local name, value = debug.getlocal(level, local_num); diff --git a/util/dependencies.lua b/util/dependencies.lua index 5baea942..53d2719d 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -136,6 +136,14 @@ function log_warnings() log("error", "This version of LuaSec contains a known bug that causes disconnects, see http://prosody.im/doc/depends"); end end + if lxp then + if not pcall(lxp.new, { StartDoctypeDecl = false }) then + log("error", "The version of LuaExpat on your system leaves Prosody " + .."vulnerable to denial-of-service attacks. You should upgrade to " + .."LuaExpat 1.1.1 or higher as soon as possible. See " + .."http://prosody.im/doc/depends#luaexpat for more information."); + end + end end return _M; diff --git a/util/ip.lua b/util/ip.lua new file mode 100644 index 00000000..2f09c034 --- /dev/null +++ b/util/ip.lua @@ -0,0 +1,176 @@ +-- Prosody IM +-- Copyright (C) 2008-2011 Florian Zeitz +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local ip_methods = {}; +local ip_mt = { __index = function (ip, key) return (ip_methods[key])(ip); end, + __tostring = function (ip) return ip.addr; end, + __eq = function (ipA, ipB) return ipA.addr == ipB.addr; end}; +local hex2bits = { ["0"] = "0000", ["1"] = "0001", ["2"] = "0010", ["3"] = "0011", ["4"] = "0100", ["5"] = "0101", ["6"] = "0110", ["7"] = "0111", ["8"] = "1000", ["9"] = "1001", ["A"] = "1010", ["B"] = "1011", ["C"] = "1100", ["D"] = "1101", ["E"] = "1110", ["F"] = "1111" }; + +local function new_ip(ipStr, proto) + if proto ~= "IPv4" and proto ~= "IPv6" then + return nil, "invalid protocol"; + end + + return setmetatable({ addr = ipStr, proto = proto }, ip_mt); +end + +local function toBits(ip) + local result = ""; + local fields = {}; + if ip.proto == "IPv4" then + ip = ip.toV4mapped; + end + ip = (ip.addr):upper(); + ip:gsub("([^:]*):?", function (c) fields[#fields + 1] = c end); + if not ip:match(":$") then fields[#fields] = nil; end + for i, field in ipairs(fields) do + if field:len() == 0 and i ~= 1 and i ~= #fields then + for i = 1, 16 * (9 - #fields) do + result = result .. "0"; + end + else + for i = 1, 4 - field:len() do + result = result .. "0000"; + end + for i = 1, field:len() do + result = result .. hex2bits[field:sub(i,i)]; + end + end + end + return result; +end + +local function commonPrefixLength(ipA, ipB) + ipA, ipB = toBits(ipA), toBits(ipB); + for i = 1, 128 do + if ipA:sub(i,i) ~= ipB:sub(i,i) then + return i-1; + end + end + return 128; +end + +local function v4scope(ip) + local fields = {}; + ip:gsub("([^.]*).?", function (c) fields[#fields + 1] = tonumber(c) end); + -- Loopback: + if fields[1] == 127 then + return 0x2; + -- Link-local unicast: + elseif fields[1] == 169 and fields[2] == 254 then + return 0x2; + -- Site-local unicast: + elseif (fields[1] == 10) or (fields[1] == 192 and fields[2] == 168) or (fields[1] == 172 and (fields[2] >= 16 and fields[2] < 32)) then + return 0x5; + -- Global unicast: + else + return 0xE; + end +end + +local function v6scope(ip) + -- Loopback: + if ip:match("^[0:]*1$") then + return 0x2; + -- Link-local unicast: + elseif ip:match("^[Ff][Ee][89ABab]") then + return 0x2; + -- Site-local unicast: + elseif ip:match("^[Ff][Ee][CcDdEeFf]") then + return 0x5; + -- Multicast: + elseif ip:match("^[Ff][Ff]") then + return tonumber("0x"..ip:sub(4,4)); + -- Global unicast: + else + return 0xE; + end +end + +local function label(ip) + if commonPrefixLength(ip, new_ip("::1", "IPv6")) == 128 then + return 0; + elseif commonPrefixLength(ip, new_ip("2002::", "IPv6")) >= 16 then + return 2; + elseif commonPrefixLength(ip, new_ip("::", "IPv6")) >= 96 then + return 3; + elseif commonPrefixLength(ip, new_ip("::ffff:0:0", "IPv6")) >= 96 then + return 4; + else + return 1; + end +end + +local function precedence(ip) + if commonPrefixLength(ip, new_ip("::1", "IPv6")) == 128 then + return 50; + elseif commonPrefixLength(ip, new_ip("2002::", "IPv6")) >= 16 then + return 30; + elseif commonPrefixLength(ip, new_ip("::", "IPv6")) >= 96 then + return 20; + elseif commonPrefixLength(ip, new_ip("::ffff:0:0", "IPv6")) >= 96 then + return 10; + else + return 40; + end +end + +local function toV4mapped(ip) + local fields = {}; + local ret = "::ffff:"; + ip:gsub("([^.]*).?", function (c) fields[#fields + 1] = tonumber(c) end); + ret = ret .. ("%02x"):format(fields[1]); + ret = ret .. ("%02x"):format(fields[2]); + ret = ret .. ":" + ret = ret .. ("%02x"):format(fields[3]); + ret = ret .. ("%02x"):format(fields[4]); + return new_ip(ret, "IPv6"); +end + +function ip_methods:toV4mapped() + if self.proto ~= "IPv4" then return nil, "No IPv4 address" end + local value = toV4mapped(self.addr); + self.toV4mapped = value; + return value; +end + +function ip_methods:label() + local value; + if self.proto == "IPv4" then + value = label(self.toV4mapped); + else + value = label(self); + end + self.label = value; + return value; +end + +function ip_methods:precedence() + local value; + if self.proto == "IPv4" then + value = precedence(self.toV4mapped); + else + value = precedence(self); + end + self.precedence = value; + return value; +end + +function ip_methods:scope() + local value; + if self.proto == "IPv4" then + value = v4scope(self.addr); + else + value = v6scope(self.addr); + end + self.scope = value; + return value; +end + +return {new_ip = new_ip, + commonPrefixLength = commonPrefixLength}; diff --git a/util/iterators.lua b/util/iterators.lua index 2a87e97a..aa0b172b 100644 --- a/util/iterators.lua +++ b/util/iterators.lua @@ -140,7 +140,7 @@ end -- Treat the return of an iterator as key,value pairs, -- and build a table function it2table(f, s, var) - local t, var = {}; + local t, var2 = {}; while true do var, var2 = f(s, var); if var == nil then break; end diff --git a/util/rfc3484.lua b/util/rfc3484.lua new file mode 100644 index 00000000..dd855a84 --- /dev/null +++ b/util/rfc3484.lua @@ -0,0 +1,133 @@ +-- Prosody IM +-- Copyright (C) 2008-2011 Florian Zeitz +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local commonPrefixLength = require"util.ip".commonPrefixLength +local new_ip = require"util.ip".new_ip; + +local function t_sort(t, comp) + for i = 1, (#t - 1) do + for j = (i + 1), #t do + local a, b = t[i], t[j]; + if not comp(a,b) then + t[i], t[j] = b, a; + end + end + end +end + +function source(dest, candidates) + local function comp(ipA, ipB) + -- Rule 1: Prefer same address + if dest == ipA then + return true; + elseif dest == ipB then + return false; + end + + -- Rule 2: Prefer appropriate scope + if ipA.scope < ipB.scope then + if ipA.scope < dest.scope then + return false; + else + return true; + end + elseif ipA.scope > ipB.scope then + if ipB.scope < dest.scope then + return true; + else + return false; + end + end + + -- Rule 3: Avoid deprecated addresses + -- XXX: No way to determine this + -- Rule 4: Prefer home addresses + -- XXX: Mobility Address related, no way to determine this + -- Rule 5: Prefer outgoing interface + -- XXX: Interface to address relation. No way to determine this + -- Rule 6: Prefer matching label + if ipA.label == dest.label and ipB.label ~= dest.label then + return true; + elseif ipB.label == dest.label and ipA.label ~= dest.label then + return false; + end + + -- Rule 7: Prefer public addresses (over temporary ones) + -- XXX: No way to determine this + -- Rule 8: Use longest matching prefix + if commonPrefixLength(ipA, dest) > commonPrefixLength(ipB, dest) then + return true; + else + return false; + end + end + + t_sort(candidates, comp); + return candidates[1]; +end + +function destination(candidates, sources) + local sourceAddrs = {}; + local function comp(ipA, ipB) + local ipAsource = sourceAddrs[ipA]; + local ipBsource = sourceAddrs[ipB]; + -- Rule 1: Avoid unusable destinations + -- XXX: No such information + -- Rule 2: Prefer matching scope + if ipA.scope == ipAsource.scope and ipB.scope ~= ipBsource.scope then + return true; + elseif ipA.scope ~= ipAsource.scope and ipB.scope == ipBsource.scope then + return false; + end + + -- Rule 3: Avoid deprecated addresses + -- XXX: No way to determine this + -- Rule 4: Prefer home addresses + -- XXX: Mobility Address related, no way to determine this + -- Rule 5: Prefer matching label + if ipAsource.label == ipA.label and ipBsource.label ~= ipB.label then + return true; + elseif ipBsource.label == ipB.label and ipAsource.label ~= ipA.label then + return false; + end + + -- Rule 6: Prefer higher precedence + if ipA.precedence > ipB.precedence then + return true; + elseif ipA.precedence < ipB.precedence then + return false; + end + + -- Rule 7: Prefer native transport + -- XXX: No way to determine this + -- Rule 8: Prefer smaller scope + if ipA.scope < ipB.scope then + return true; + elseif ipA.scope > ipB.scope then + return false; + end + + -- Rule 9: Use longest matching prefix + if commonPrefixLength(ipA, ipAsource) > commonPrefixLength(ipB, ipBsource) then + return true; + elseif commonPrefixLength(ipA, ipAsource) < commonPrefixLength(ipB, ipBsource) then + return false; + end + + -- Rule 10: Otherwise, leave order unchanged + return true; + end + for _, ip in ipairs(candidates) do + sourceAddrs[ip] = source(ip, sources); + end + + t_sort(candidates, comp); + return candidates; +end + +return {source = source, + destination = destination}; diff --git a/util/stanza.lua b/util/stanza.lua index de83977f..600212a4 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -258,11 +258,6 @@ function stanza_mt.get_error(stanza) return type, condition or "undefined-condition", text; end -function stanza_mt.__add(s1, s2) - return s1:add_direct_child(s2); -end - - do local id = 0; function new_id() diff --git a/util/xmppstream.lua b/util/xmppstream.lua index e5271b72..0f80742d 100644 --- a/util/xmppstream.lua +++ b/util/xmppstream.lua @@ -11,26 +11,16 @@ local lxp = require "lxp"; local st = require "util.stanza"; local stanza_mt = st.stanza_mt; +local error = error; local tostring = tostring; local t_insert = table.insert; local t_concat = table.concat; local t_remove = table.remove; local setmetatable = setmetatable; -local default_log = require "util.logger".init("xmppstream"); - -- COMPAT: w/LuaExpat 1.1.0 local lxp_supports_doctype = pcall(lxp.new, { StartDoctypeDecl = false }); -if not lxp_supports_doctype then - default_log("warn", "The version of LuaExpat on your system leaves Prosody " - .."vulnerable to denial-of-service attacks. You should upgrade to " - .."LuaExpat 1.1.1 or higher as soon as possible. See " - .."http://prosody.im/doc/depends#luaexpat for more information."); -end - -local error = error; - module "xmppstream" local new_parser = lxp.new; @@ -50,8 +40,6 @@ _M.ns_pattern = ns_pattern; function new_sax_handlers(session, stream_callbacks) local xml_handlers = {}; - local log = session.log or default_log; - local cb_streamopened = stream_callbacks.streamopened; local cb_streamclosed = stream_callbacks.streamclosed; local cb_error = stream_callbacks.error or function(session, e) error("XML stream error: "..tostring(e)); end; @@ -188,7 +176,6 @@ function new_sax_handlers(session, stream_callbacks) local function set_session(stream, new_session) session = new_session; - log = new_session.log or default_log; end return xml_handlers, { reset = reset, set_session = set_session }; |