Mercurial > prosody-modules
diff mod_client_proxy/mod_client_proxy.lua @ 3098:a81456a13797
mod_client_proxy: a Jabber Address Translation implementation
author | Jonas Wielicki <jonas@wielicki.name> |
---|---|
date | Sun, 03 Jun 2018 13:55:10 +0200 |
parents | |
children | a14a573d43ff |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_client_proxy/mod_client_proxy.lua Sun Jun 03 13:55:10 2018 +0200 @@ -0,0 +1,208 @@ +if module:get_host_type() ~= "component" then + error("proxy_component should be loaded as component", 0); +end + +local comp_host = module:get_host(); +local comp_name = module:get_option_string("name", "Proxy Component"); + +local jid_split = require "util.jid".split; +local jid_bare = require "util.jid".bare; +local jid_prep = require "util.jid".prep; +local st = require "util.stanza"; +local array = require "util.array"; + +local target_address = module:get_option_string("target_address"); + +local hosts = prosody.hosts; + +sessions = array{}; +local sessions = sessions; + +local function handle_target_presence(origin, stanza) + local type = stanza.attr.type; + module:log("debug", "received presence from destination: %s", type) + local _, _, resource = jid_split(stanza.attr.from); + if type == "error" then + -- drop all known sessions + for k in pairs(sessions) do + sessions[k] = nil + end + module:log( + "debug", + "received error presence, dropping all target sessions", + resource + ) + elseif type == "unavailable" then + for k in pairs(sessions) do + if sessions[k] == resource then + sessions[k] = nil + module:log( + "debug", + "dropped target session: %s", + resource + ) + break + end + end + elseif not type then + -- available + local found = false; + for k in pairs(sessions) do + if sessions[k] == resource then + found = true; + break + end + end + if not found then + module:log( + "debug", + "registered new target session: %s", + resource + ) + sessions:push(resource) + end + end +end + +local function handle_from_target(origin, stanza) + local type = stanza.attr.type + module:log( + "debug", + "non-presence stanza from target: name = %s, type = %s", + stanza.name, + type + ) + if stanza.name == "iq" then + if type == "error" or type == "result" then + -- de-NAT message + local _, _, denatted_to_unprepped = jid_split(stanza.attr.to); + local denatted_to = jid_prep(denatted_to_unprepped); + if not denatted_to then + module:log( + "debug", + "cannot de-NAT stanza, invalid to: %s", + denatted_to_unprepped + ) + return + end + local denatted_from = module:get_host(); + + module:log( + "debug", + "de-NAT-ed stanza: from: %s -> %s, to: %s -> %s", + stanza.attr.from, + denatted_from, + stanza.attr.to, + denatted_to + ) + + stanza.attr.from = denatted_from + stanza.attr.to = denatted_to + + module:send(stanza) + else + -- FIXME: we don’t support NATing outbund requests atm. + module:send(st.error_reply(stanza, "cancel", "feature-not-implemented")) + end + elseif stanza.name == "message" then + -- not implemented yet, we need a way to ensure that routing doesn’t + -- break + module:send(st.error_reply(stanza, "cancel", "feature-not-implemented")) + end +end + +local function handle_to_target(origin, stanza) + local type = stanza.attr.type; + module:log( + "debug", + "stanza to target: name = %s, type = %s", + stanza.name, type + ) + if stanza.name == "presence" then + if type ~= "error" then + module:send(st.error_reply(stanza, "cancel", "bad-request")) + return + end + elseif stanza.name == "iq" then + if type == "get" or type == "set" then + if #sessions == 0 then + -- no sessions available to send to + module:log("debug", "no sessions to send to!") + module:send(st.error_reply(stanza, "cancel", "service-unavailable")) + return + end + + -- find a target session + local target_session = sessions:random() + local target = target_address .. "/" .. target_session + + -- encode sender JID in resource + local natted_from = module:get_host() .. "/" .. stanza.attr.from; + + module:log( + "debug", + "NAT-ed stanza: from: %s -> %s, to: %s -> %s", + stanza.attr.from, + natted_from, + stanza.attr.to, + target + ) + + stanza.attr.from = natted_from + stanza.attr.to = target + + module:send(stanza) + else + -- FIXME: handle and forward result/error correctly + end + elseif stanza.name == "message" then + -- not implemented yet, we need a way to ensure that routing doesn’t + -- break + module:send(st.error_reply(stanza, "cancel", "feature-not-implemented")) + end +end + +local function stanza_handler(event) + local origin, stanza = event.origin, event.stanza + module:log("debug", "received stanza from %s session", origin.type) + + local bare_from = jid_bare(stanza.attr.from); + local _, _, to = jid_split(stanza.attr.to); + if bare_from == target_address then + -- from our target, to whom? + if not to then + -- directly to component + if stanza.name == "presence" then + handle_target_presence(origin, stanza) + else + module:send(st.error_reply(stanza, "cancel", "bad-request")) + return true + end + else + -- to someone else + handle_from_target(origin, stanza) + end + else + handle_to_target(origin, stanza) + end + return true +end + +module:hook("iq/bare", stanza_handler, -1); +module:hook("message/bare", stanza_handler, -1); +module:hook("presence/bare", stanza_handler, -1); +module:hook("iq/full", stanza_handler, -1); +module:hook("message/full", stanza_handler, -1); +module:hook("presence/full", stanza_handler, -1); +module:hook("iq/host", stanza_handler, -1); +module:hook("message/host", stanza_handler, -1); +module:hook("presence/host", stanza_handler, -1); + +module:log("debug", "loaded proxy on %s", module:get_host()) + +subscription_request = st.presence({ + type = "subscribe", + to = target_address, + from = module:get_host()} +) +module:send(subscription_request)