Software /
code /
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 |
child | 3101: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) |