Mercurial > 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 |
| parents | b86ae5e21a56 |
| children | 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 |
