Mercurial > prosody-modules
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 3097:c7f4e3987ed0 | 3098:a81456a13797 |
|---|---|
| 1 if module:get_host_type() ~= "component" then | |
| 2 error("proxy_component should be loaded as component", 0); | |
| 3 end | |
| 4 | |
| 5 local comp_host = module:get_host(); | |
| 6 local comp_name = module:get_option_string("name", "Proxy Component"); | |
| 7 | |
| 8 local jid_split = require "util.jid".split; | |
| 9 local jid_bare = require "util.jid".bare; | |
| 10 local jid_prep = require "util.jid".prep; | |
| 11 local st = require "util.stanza"; | |
| 12 local array = require "util.array"; | |
| 13 | |
| 14 local target_address = module:get_option_string("target_address"); | |
| 15 | |
| 16 local hosts = prosody.hosts; | |
| 17 | |
| 18 sessions = array{}; | |
| 19 local sessions = sessions; | |
| 20 | |
| 21 local function handle_target_presence(origin, stanza) | |
| 22 local type = stanza.attr.type; | |
| 23 module:log("debug", "received presence from destination: %s", type) | |
| 24 local _, _, resource = jid_split(stanza.attr.from); | |
| 25 if type == "error" then | |
| 26 -- drop all known sessions | |
| 27 for k in pairs(sessions) do | |
| 28 sessions[k] = nil | |
| 29 end | |
| 30 module:log( | |
| 31 "debug", | |
| 32 "received error presence, dropping all target sessions", | |
| 33 resource | |
| 34 ) | |
| 35 elseif type == "unavailable" then | |
| 36 for k in pairs(sessions) do | |
| 37 if sessions[k] == resource then | |
| 38 sessions[k] = nil | |
| 39 module:log( | |
| 40 "debug", | |
| 41 "dropped target session: %s", | |
| 42 resource | |
| 43 ) | |
| 44 break | |
| 45 end | |
| 46 end | |
| 47 elseif not type then | |
| 48 -- available | |
| 49 local found = false; | |
| 50 for k in pairs(sessions) do | |
| 51 if sessions[k] == resource then | |
| 52 found = true; | |
| 53 break | |
| 54 end | |
| 55 end | |
| 56 if not found then | |
| 57 module:log( | |
| 58 "debug", | |
| 59 "registered new target session: %s", | |
| 60 resource | |
| 61 ) | |
| 62 sessions:push(resource) | |
| 63 end | |
| 64 end | |
| 65 end | |
| 66 | |
| 67 local function handle_from_target(origin, stanza) | |
| 68 local type = stanza.attr.type | |
| 69 module:log( | |
| 70 "debug", | |
| 71 "non-presence stanza from target: name = %s, type = %s", | |
| 72 stanza.name, | |
| 73 type | |
| 74 ) | |
| 75 if stanza.name == "iq" then | |
| 76 if type == "error" or type == "result" then | |
| 77 -- de-NAT message | |
| 78 local _, _, denatted_to_unprepped = jid_split(stanza.attr.to); | |
| 79 local denatted_to = jid_prep(denatted_to_unprepped); | |
| 80 if not denatted_to then | |
| 81 module:log( | |
| 82 "debug", | |
| 83 "cannot de-NAT stanza, invalid to: %s", | |
| 84 denatted_to_unprepped | |
| 85 ) | |
| 86 return | |
| 87 end | |
| 88 local denatted_from = module:get_host(); | |
| 89 | |
| 90 module:log( | |
| 91 "debug", | |
| 92 "de-NAT-ed stanza: from: %s -> %s, to: %s -> %s", | |
| 93 stanza.attr.from, | |
| 94 denatted_from, | |
| 95 stanza.attr.to, | |
| 96 denatted_to | |
| 97 ) | |
| 98 | |
| 99 stanza.attr.from = denatted_from | |
| 100 stanza.attr.to = denatted_to | |
| 101 | |
| 102 module:send(stanza) | |
| 103 else | |
| 104 -- FIXME: we don’t support NATing outbund requests atm. | |
| 105 module:send(st.error_reply(stanza, "cancel", "feature-not-implemented")) | |
| 106 end | |
| 107 elseif stanza.name == "message" then | |
| 108 -- not implemented yet, we need a way to ensure that routing doesn’t | |
| 109 -- break | |
| 110 module:send(st.error_reply(stanza, "cancel", "feature-not-implemented")) | |
| 111 end | |
| 112 end | |
| 113 | |
| 114 local function handle_to_target(origin, stanza) | |
| 115 local type = stanza.attr.type; | |
| 116 module:log( | |
| 117 "debug", | |
| 118 "stanza to target: name = %s, type = %s", | |
| 119 stanza.name, type | |
| 120 ) | |
| 121 if stanza.name == "presence" then | |
| 122 if type ~= "error" then | |
| 123 module:send(st.error_reply(stanza, "cancel", "bad-request")) | |
| 124 return | |
| 125 end | |
| 126 elseif stanza.name == "iq" then | |
| 127 if type == "get" or type == "set" then | |
| 128 if #sessions == 0 then | |
| 129 -- no sessions available to send to | |
| 130 module:log("debug", "no sessions to send to!") | |
| 131 module:send(st.error_reply(stanza, "cancel", "service-unavailable")) | |
| 132 return | |
| 133 end | |
| 134 | |
| 135 -- find a target session | |
| 136 local target_session = sessions:random() | |
| 137 local target = target_address .. "/" .. target_session | |
| 138 | |
| 139 -- encode sender JID in resource | |
| 140 local natted_from = module:get_host() .. "/" .. stanza.attr.from; | |
| 141 | |
| 142 module:log( | |
| 143 "debug", | |
| 144 "NAT-ed stanza: from: %s -> %s, to: %s -> %s", | |
| 145 stanza.attr.from, | |
| 146 natted_from, | |
| 147 stanza.attr.to, | |
| 148 target | |
| 149 ) | |
| 150 | |
| 151 stanza.attr.from = natted_from | |
| 152 stanza.attr.to = target | |
| 153 | |
| 154 module:send(stanza) | |
| 155 else | |
| 156 -- FIXME: handle and forward result/error correctly | |
| 157 end | |
| 158 elseif stanza.name == "message" then | |
| 159 -- not implemented yet, we need a way to ensure that routing doesn’t | |
| 160 -- break | |
| 161 module:send(st.error_reply(stanza, "cancel", "feature-not-implemented")) | |
| 162 end | |
| 163 end | |
| 164 | |
| 165 local function stanza_handler(event) | |
| 166 local origin, stanza = event.origin, event.stanza | |
| 167 module:log("debug", "received stanza from %s session", origin.type) | |
| 168 | |
| 169 local bare_from = jid_bare(stanza.attr.from); | |
| 170 local _, _, to = jid_split(stanza.attr.to); | |
| 171 if bare_from == target_address then | |
| 172 -- from our target, to whom? | |
| 173 if not to then | |
| 174 -- directly to component | |
| 175 if stanza.name == "presence" then | |
| 176 handle_target_presence(origin, stanza) | |
| 177 else | |
| 178 module:send(st.error_reply(stanza, "cancel", "bad-request")) | |
| 179 return true | |
| 180 end | |
| 181 else | |
| 182 -- to someone else | |
| 183 handle_from_target(origin, stanza) | |
| 184 end | |
| 185 else | |
| 186 handle_to_target(origin, stanza) | |
| 187 end | |
| 188 return true | |
| 189 end | |
| 190 | |
| 191 module:hook("iq/bare", stanza_handler, -1); | |
| 192 module:hook("message/bare", stanza_handler, -1); | |
| 193 module:hook("presence/bare", stanza_handler, -1); | |
| 194 module:hook("iq/full", stanza_handler, -1); | |
| 195 module:hook("message/full", stanza_handler, -1); | |
| 196 module:hook("presence/full", stanza_handler, -1); | |
| 197 module:hook("iq/host", stanza_handler, -1); | |
| 198 module:hook("message/host", stanza_handler, -1); | |
| 199 module:hook("presence/host", stanza_handler, -1); | |
| 200 | |
| 201 module:log("debug", "loaded proxy on %s", module:get_host()) | |
| 202 | |
| 203 subscription_request = st.presence({ | |
| 204 type = "subscribe", | |
| 205 to = target_address, | |
| 206 from = module:get_host()} | |
| 207 ) | |
| 208 module:send(subscription_request) |
