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)