Software /
code /
prosody-modules
Comparison
mod_proxy65/mod_proxy65.lua @ 68:0df3e4d1f1a3
mod_proxy65: Reviewed and re-factored the code, added proxy_address to specify the address which the proxy advertises for clients to connect to
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Sat, 31 Oct 2009 00:41:47 +0000 |
parent | 66:b86ae5e21a56 |
child | 69:87dfd34dceb2 |
comparison
equal
deleted
inserted
replaced
67:e839b4453387 | 68:0df3e4d1f1a3 |
---|---|
20 local connlisteners_deregister = require "net.connlisteners".deregister; | 20 local connlisteners_deregister = require "net.connlisteners".deregister; |
21 local adns, dns = require "net.adns", require "net.dns"; | 21 local adns, dns = require "net.adns", require "net.dns"; |
22 local add_task = require "util.timer".add_task; | 22 local add_task = require "util.timer".add_task; |
23 local max_dns_depth = config.get("*", "core", "dns_max_depth") or 3; | 23 local max_dns_depth = config.get("*", "core", "dns_max_depth") or 3; |
24 local dns_timeout = config.get("*", "core", "dns_timeout") or 60; | 24 local dns_timeout = config.get("*", "core", "dns_timeout") or 60; |
25 local serialize = require "util.serialization".serialize; | |
26 local sha1 = require "util.hashes".sha1; | 25 local sha1 = require "util.hashes".sha1; |
27 | 26 |
28 local replies_cache = {}; | 27 local replies_cache = {}; |
29 local _host = module:get_host(); | 28 local host = module:get_host(); |
30 local _name = "SOCKS5 Bytestreams Service"; | 29 local name = "SOCKS5 Bytestreams Service"; |
31 local connlistener = {registered=false}; | 30 local sessions, transfers, component = {}, {}, nil; |
32 local _config = {}; | 31 |
33 local sessions = {}; | 32 local proxy_port = config_get(host, "core", "proxy65_port") or 5000; |
34 local transfers = {}; | 33 local proxy_interface = config_get(host, "core", "proxy65_interface") or "*"; |
35 local component; | 34 local proxy_address = config_get(host, "core", "proxy65_address") or (proxy_interface ~= "*" and proxy_interface) or module.host; |
36 | 35 |
37 _config.port = config_get(_host, "core", "port"); | 36 local connlistener = { |
38 _config.interface = config_get(_host, "core", "interface"); | 37 registered = false, default_port = proxy_port, |
39 | 38 default_interface = proxy_interface, default_mode = "*a" |
40 if _config.port == nil then | 39 }; |
41 _config.port = 5000; | |
42 end | |
43 | 40 |
44 local function bin2hex(bin) | 41 local function bin2hex(bin) |
45 return bin:gsub(".", function (c) return ("%02x"):format(c:byte()); end) | 42 return bin:gsub(".", function (c) return ("%02x"):format(c:byte()); end) |
46 end | 43 end |
47 | 44 |
48 function new_session(conn) | 45 function new_session(conn) |
49 local w = function(s) conn.write(s:gsub("\n", "\r\n")); end; | 46 local w = function(s) conn.write(s:gsub("\n", "\r\n")); end; |
50 local session = { conn = conn; | 47 local session = { conn = conn; |
51 send = function (t) w(tostring(t)); end; | 48 send = function (t) w(tostring(t)); end; |
52 print = function (t) w("| "..tostring(t).."\n"); end; | |
53 disconnect = function () conn.close(); end; | 49 disconnect = function () conn.close(); end; |
54 }; | 50 }; |
55 | |
56 return session; | 51 return session; |
57 end | 52 end |
58 | 53 |
59 function connlistener.listener(conn, data) | 54 function connlistener.listener(conn, data) |
60 local session = sessions[conn]; | 55 local session = sessions[conn]; |
110 end | 105 end |
111 end | 106 end |
112 end | 107 end |
113 | 108 |
114 function connlistener.disconnect(conn, err) | 109 function connlistener.disconnect(conn, err) |
115 | 110 if sessions[conn] then |
111 -- Clean up any session-related stuff here | |
112 sessions[conn] = nil; | |
113 end | |
116 end | 114 end |
117 | 115 |
118 local function get_disco_info(stanza) | 116 local function get_disco_info(stanza) |
119 local reply = replies_cache.disco_info; | 117 local reply = replies_cache.disco_info; |
120 if reply == nil then | 118 if reply == nil then |
121 reply = st.iq({type='result', from=_host}):query("http://jabber.org/protocol/disco#info") | 119 reply = st.iq({type='result', from=host}):query("http://jabber.org/protocol/disco#info") |
122 :tag("identity", {category='proxy', type='bytestreams', name=_name}):up() | 120 :tag("identity", {category='proxy', type='bytestreams', name=name}):up() |
123 :tag("feature", {var="http://jabber.org/protocol/bytestreams"}); | 121 :tag("feature", {var="http://jabber.org/protocol/bytestreams"}); |
124 replies_cache.disco_info = reply; | 122 replies_cache.disco_info = reply; |
125 end | 123 end |
126 | 124 |
127 reply.attr.id = stanza.attr.id; | 125 reply.attr.id = stanza.attr.id; |
130 end | 128 end |
131 | 129 |
132 local function get_disco_items(stanza) | 130 local function get_disco_items(stanza) |
133 local reply = replies_cache.disco_items; | 131 local reply = replies_cache.disco_items; |
134 if reply == nil then | 132 if reply == nil then |
135 reply = st.iq({type='result', from=_host}):query("http://jabber.org/protocol/disco#items"); | 133 reply = st.iq({type='result', from=host}):query("http://jabber.org/protocol/disco#items"); |
136 replies_cache.disco_info = reply; | 134 replies_cache.disco_info = reply; |
137 end | 135 end |
138 | 136 |
139 reply.attr.id = stanza.attr.id; | 137 reply.attr.id = stanza.attr.id; |
140 reply.attr.to = stanza.attr.from; | 138 reply.attr.to = stanza.attr.from; |
143 | 141 |
144 local function get_stream_host(stanza) | 142 local function get_stream_host(stanza) |
145 local reply = replies_cache.stream_host; | 143 local reply = replies_cache.stream_host; |
146 local sid = stanza.tags[1].attr.sid; | 144 local sid = stanza.tags[1].attr.sid; |
147 if reply == nil then | 145 if reply == nil then |
148 reply = st.iq({type="result", from=_host}) | 146 reply = st.iq({type="result", from=host}) |
149 :query("http://jabber.org/protocol/bytestreams") | 147 :query("http://jabber.org/protocol/bytestreams") |
150 :tag("streamhost", {jid=_host, host=_config.interface, port=_config.port}); -- TODO get the correct data | 148 :tag("streamhost", {jid=host, host=proxy_address, port=proxy_port}); -- TODO get the correct data |
151 replies_cache.stream_host = reply; | 149 replies_cache.stream_host = reply; |
152 end | 150 end |
153 | 151 |
154 reply.attr.id = stanza.attr.id; | 152 reply.attr.id = stanza.attr.id; |
155 reply.attr.to = stanza.attr.from; | 153 reply.attr.to = stanza.attr.from; |
156 reply.tags[1].attr.sid = sid; | 154 reply.tags[1].attr.sid = sid; |
157 return reply; | 155 return reply; |
158 end | 156 end |
159 | 157 |
160 module.unload = function() | 158 module.unload = function() |
161 component_deregister(_host); | 159 component_deregister(host); |
162 connlisteners_deregister("proxy65"); | 160 connlisteners_deregister("proxy65"); |
163 end | 161 end |
164 | 162 |
165 local function set_activation(stanza) | 163 local function set_activation(stanza) |
166 local from = nil; | 164 local from, to, sid, reply = nil; |
167 local to = nil; | 165 from = stanza.attr.from; |
168 local sid = nil; | |
169 local reply = nil; | |
170 if stanza.attr ~= nil then | |
171 from = stanza.attr.from; | |
172 end | |
173 if stanza.tags[1] ~= nil and tostring(stanza.tags[1].name) == "query" then | 166 if stanza.tags[1] ~= nil and tostring(stanza.tags[1].name) == "query" then |
174 if stanza.tags[1].attr ~= nil then | 167 if stanza.tags[1].attr ~= nil then |
175 sid = stanza.tags[1].attr.sid; | 168 sid = stanza.tags[1].attr.sid; |
176 end | 169 end |
177 if stanza.tags[1].tags[1] ~= nil and tostring(stanza.tags[1].tags[1].name) == "activate" then | 170 if stanza.tags[1].tags[1] ~= nil and tostring(stanza.tags[1].tags[1].name) == "activate" then |
178 to = stanza.tags[1].tags[1][1]; | 171 to = stanza.tags[1].tags[1][1]; |
179 end | 172 end |
180 end | 173 end |
181 if from ~= nil and to ~= nil and sid ~= nil then | 174 if from ~= nil and to ~= nil and sid ~= nil then |
182 reply = st.iq({type="result", from=_host}); | 175 reply = st.iq({type="result", from=host}); |
183 reply.attr.id = stanza.attr.id; | 176 reply.attr.id = stanza.attr.id; |
184 end | 177 end |
185 return reply, from, to, sid; | 178 return reply, from, to, sid; |
186 end | 179 end |
187 | 180 |
188 local function forward(initiator, target) | 181 function handle_to_domain(origin, stanza) |
189 module:log("debug", "forward it ...."); | 182 local to_node, to_host, to_resource = jid_split(stanza.attr.to); |
190 end | 183 if to_node == nil then |
191 | 184 local type = stanza.attr.type; |
192 | 185 if type == "error" or type == "result" then return; end |
193 local function register() | 186 if stanza.name == "iq" and type == "get" then |
194 connlistener.default_port = _config.port; | 187 local xmlns = stanza.tags[1].attr.xmlns |
195 connlistener.default_interface = "*"; | 188 if xmlns == "http://jabber.org/protocol/disco#info" then |
196 connlistener.default_mode = "*a"; | 189 origin.send(get_disco_info(stanza)); |
197 connlistener.registered = connlisteners_register('proxy65', connlistener); | 190 return true; |
198 if(connlistener.registered == false) then | 191 elseif xmlns == "http://jabber.org/protocol/disco#items" then |
199 error("Proxy65: Could not establish a connection listener. Check your configuration please."); | 192 origin.send(get_disco_items(stanza)); |
200 else | 193 return true; |
201 connlistener.handler = connlisteners_start('proxy65'); | 194 elseif xmlns == "http://jabber.org/protocol/bytestreams" then |
202 module:add_item("proxy65", {jid=_host, name=_name}) | 195 origin.send(get_stream_host(stanza)); |
203 component = component_register(_host, function(origin, stanza) | 196 return true; |
204 local to_node, to_host, to_resource = jid_split(stanza.attr.to); | 197 end |
205 if to_node == nil then | 198 elseif stanza.name == "iq" and type == "set" then |
206 local type = stanza.attr.type; | 199 local reply, from, to, sid = set_activation(stanza); |
207 if type == "error" or type == "result" then return; end | 200 if reply ~= nil and from ~= nil and to ~= nil and sid ~= nil then |
208 if stanza.name == "iq" and type == "get" then | 201 local sha = sha1(sid .. from .. to, true); |
209 local xmlns = stanza.tags[1].attr.xmlns | 202 if transfers[sha] == nil then |
210 if xmlns == "http://jabber.org/protocol/disco#info" then | 203 module:log("error", "transfers[sha]: nil"); |
211 origin.send(get_disco_info(stanza)); | 204 elseif(transfers[sha] ~= nil and transfers[sha].initiator ~= nil and transfers[sha].target ~= nil) then |
212 return true; | 205 origin.send(reply); |
213 elseif xmlns == "http://jabber.org/protocol/disco#items" then | 206 transfers[sha].activated = true; |
214 origin.send(get_disco_items(stanza)); | |
215 return true; | |
216 elseif xmlns == "http://jabber.org/protocol/bytestreams" then | |
217 origin.send(get_stream_host(stanza)); | |
218 return true; | |
219 end | |
220 elseif stanza.name == "iq" and type == "set" then | |
221 local reply, from, to, sid = set_activation(stanza); | |
222 if reply ~= nil and from ~= nil and to ~= nil and sid ~= nil then | |
223 local sha = sha1(sid .. from .. to, true); | |
224 if transfers[sha] == nil then | |
225 module:log("error", "transfers[sha]: nil"); | |
226 elseif(transfers[sha] ~= nil and transfers[sha].initiator ~= nil and transfers[sha].target ~= nil) then | |
227 origin.send(reply); | |
228 transfers[sha].activated = true; | |
229 end | |
230 end | |
231 end | 207 end |
232 end | 208 end |
233 return; | 209 end |
234 end); | 210 end |
235 end | 211 return; |
236 end | 212 end |
237 | 213 |
238 local function getDefaultIP(host) | 214 if not connlisteners_register('proxy65', connlistener) then |
239 local handle; | 215 error("mod_proxy65: Could not establish a connection listener. Check your configuration please."); |
240 handle = adns.lookup(function (reply) | 216 end |
241 handle = nil; | 217 |
242 | 218 connlisteners_start('proxy65'); |
243 -- COMPAT: This is a compromise for all you CNAME-(ab)users :) | 219 component = component_register(host, handle_to_domain); |
244 if not (reply and reply[#reply] and reply[#reply].a) then | |
245 local count = max_dns_depth; | |
246 reply = dns.peek(host, "CNAME", "IN"); | |
247 while count > 0 and reply and reply[#reply] and not reply[#reply].a and reply[#reply].cname do | |
248 module:log("debug", "Looking up %s (DNS depth is %d)", tostring(reply[#reply].cname), count); | |
249 reply = dns.peek(reply[#reply].cname, "A", "IN") or dns.peek(reply[#reply].cname, "CNAME", "IN"); | |
250 count = count - 1; | |
251 end | |
252 end | |
253 -- end of CNAME resolving | |
254 | |
255 if reply and reply[#reply] and reply[#reply].a then | |
256 module:log("debug", "DNS reply for %s gives us %s", host, reply[#reply].a); | |
257 _config.interface = reply[#reply].a | |
258 return register(); | |
259 else | |
260 module:log("debug", "DNS lookup failed to get a response for %s", host); | |
261 if host:find(".") ~= nil then | |
262 host = host:gsub("^[^%.]*%.", ""); | |
263 if host:find(".") ~= nil then -- still one dot left? | |
264 return getDefaultIP(host); | |
265 end | |
266 end | |
267 error("Proxy65: Could not get an interface to bind to. Please configure one."); | |
268 end | |
269 end, host, "A", "IN"); | |
270 | |
271 -- Set handler for DNS timeout | |
272 add_task(dns_timeout, function () | |
273 if handle then | |
274 adns.cancel(handle, true); | |
275 end | |
276 end); | |
277 return true; | |
278 end | |
279 | |
280 if _config.interface ~= nil then | |
281 register(); | |
282 else | |
283 getDefaultIP(_host); -- try to DNS lookup module:host() | |
284 end |