Software / code / prosody
Comparison
plugins/mod_proxy65.lua @ 4679:5b52b5eaa03d
mod_proxy65: Port to portmanager, make a shared module
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Tue, 24 Apr 2012 18:50:22 +0100 |
| parent | 4414:aa2e79f20962 |
| child | 4685:3d90264c7b3d |
comparison
equal
deleted
inserted
replaced
| 4678:9613673f916a | 4679:5b52b5eaa03d |
|---|---|
| 4 -- Copyright (C) 2009 Thilo Cestonaro | 4 -- Copyright (C) 2009 Thilo Cestonaro |
| 5 -- | 5 -- |
| 6 -- This project is MIT/X11 licensed. Please see the | 6 -- This project is MIT/X11 licensed. Please see the |
| 7 -- COPYING file in the source package for more information. | 7 -- COPYING file in the source package for more information. |
| 8 -- | 8 -- |
| 9 --[[ | |
| 10 * to restart the proxy in the console: e.g. | |
| 11 module:unload("proxy65"); | |
| 12 > server.removeserver(<proxy65_port>); | |
| 13 module:load("proxy65", <proxy65_jid>); | |
| 14 ]]-- | |
| 15 | 9 |
| 10 module:set_global(); | |
| 16 | 11 |
| 17 local module = module; | |
| 18 local tostring = tostring; | |
| 19 local jid_compare, jid_prep = require "util.jid".compare, require "util.jid".prep; | 12 local jid_compare, jid_prep = require "util.jid".compare, require "util.jid".prep; |
| 20 local st = require "util.stanza"; | 13 local st = require "util.stanza"; |
| 21 local connlisteners = require "net.connlisteners"; | |
| 22 local sha1 = require "util.hashes".sha1; | 14 local sha1 = require "util.hashes".sha1; |
| 15 local b64 = require "util.encodings".base64.encode; | |
| 23 local server = require "net.server"; | 16 local server = require "net.server"; |
| 24 local b64 = require "util.encodings".base64.encode; | |
| 25 | 17 |
| 26 local host, name = module:get_host(), "SOCKS5 Bytestreams Service"; | 18 local sessions, transfers = module:shared("sessions", "transfers"); |
| 27 local sessions, transfers = {}, {}; | |
| 28 | |
| 29 local proxy_port = module:get_option("proxy65_port") or 5000; | |
| 30 local proxy_interface = module:get_option("proxy65_interface") or "*"; | |
| 31 local proxy_address = module:get_option("proxy65_address") or (proxy_interface ~= "*" and proxy_interface) or host; | |
| 32 local proxy_acl = module:get_option("proxy65_acl"); | |
| 33 local max_buffer_size = 4096; | 19 local max_buffer_size = 4096; |
| 34 | 20 |
| 35 local connlistener = { default_port = proxy_port, default_interface = proxy_interface, default_mode = "*a" }; | 21 local listener = {}; |
| 36 | 22 |
| 37 function connlistener.onincoming(conn, data) | 23 function listener.onincoming(conn, data) |
| 38 local session = sessions[conn] or {}; | 24 local session = sessions[conn] or {}; |
| 39 | 25 |
| 40 local transfer = transfers[session.sha]; | 26 local transfer = transfers[session.sha]; |
| 41 if transfer and transfer.activated then -- copy data between initiator and target | 27 if transfer and transfer.activated then -- copy data between initiator and target |
| 42 local initiator, target = transfer.initiator, transfer.target; | 28 local initiator, target = transfer.initiator, transfer.target; |
| 82 module:log("debug", "Invalid SOCKS5 negotiation recieved: '%s'", b64(data)); | 68 module:log("debug", "Invalid SOCKS5 negotiation recieved: '%s'", b64(data)); |
| 83 end | 69 end |
| 84 end | 70 end |
| 85 end | 71 end |
| 86 | 72 |
| 87 function connlistener.ondisconnect(conn, err) | 73 function listener.ondisconnect(conn, err) |
| 88 local session = sessions[conn]; | 74 local session = sessions[conn]; |
| 89 if session then | 75 if session then |
| 90 if transfers[session.sha] then | 76 if transfers[session.sha] then |
| 91 local initiator, target = transfers[session.sha].initiator, transfers[session.sha].target; | 77 local initiator, target = transfers[session.sha].initiator, transfers[session.sha].target; |
| 92 if initiator == conn and target ~= nil then | 78 if initiator == conn and target ~= nil then |
| 99 -- Clean up any session-related stuff here | 85 -- Clean up any session-related stuff here |
| 100 sessions[conn] = nil; | 86 sessions[conn] = nil; |
| 101 end | 87 end |
| 102 end | 88 end |
| 103 | 89 |
| 104 module:add_identity("proxy", "bytestreams", name); | 90 function module.add_host(module) |
| 105 module:add_feature("http://jabber.org/protocol/bytestreams"); | 91 local host, name = module:get_host(), module:get_option_string("name", "SOCKS5 Bytestreams Service"); |
| 92 | |
| 93 local proxy_address = module:get_option("proxy65_address", host); | |
| 94 local proxy_port = module:get_option_number("proxy65_port", next(portmanager.get_active_services():search("proxy65", nil)[1])); | |
| 95 local proxy_acl = module:get_option("proxy65_acl"); | |
| 106 | 96 |
| 107 module:hook("iq-get/host/http://jabber.org/protocol/disco#info:query", function(event) | 97 module:add_identity("proxy", "bytestreams", name); |
| 108 local origin, stanza = event.origin, event.stanza; | 98 module:add_feature("http://jabber.org/protocol/bytestreams"); |
| 109 origin.send(st.reply(stanza):query("http://jabber.org/protocol/disco#info") | |
| 110 :tag("identity", {category='proxy', type='bytestreams', name=name}):up() | |
| 111 :tag("feature", {var="http://jabber.org/protocol/bytestreams"}) ); | |
| 112 return true; | |
| 113 end, -1); | |
| 114 | |
| 115 module:hook("iq-get/host/http://jabber.org/protocol/disco#items:query", function(event) | |
| 116 local origin, stanza = event.origin, event.stanza; | |
| 117 origin.send(st.reply(stanza):query("http://jabber.org/protocol/disco#items")); | |
| 118 return true; | |
| 119 end, -1); | |
| 120 | |
| 121 module:hook("iq-get/host/http://jabber.org/protocol/bytestreams:query", function(event) | |
| 122 local origin, stanza = event.origin, event.stanza; | |
| 123 | 99 |
| 124 -- check ACL | 100 module:hook("iq-get/host/http://jabber.org/protocol/disco#info:query", function(event) |
| 125 while proxy_acl and #proxy_acl > 0 do -- using 'while' instead of 'if' so we can break out of it | 101 local origin, stanza = event.origin, event.stanza; |
| 126 local jid = stanza.attr.from; | 102 origin.send(st.reply(stanza):query("http://jabber.org/protocol/disco#info") |
| 127 for _, acl in ipairs(proxy_acl) do | 103 :tag("identity", {category='proxy', type='bytestreams', name=name}):up() |
| 128 if jid_compare(jid, acl) then break; end | 104 :tag("feature", {var="http://jabber.org/protocol/bytestreams"}) ); |
| 105 return true; | |
| 106 end, -1); | |
| 107 | |
| 108 module:hook("iq-get/host/http://jabber.org/protocol/disco#items:query", function(event) | |
| 109 local origin, stanza = event.origin, event.stanza; | |
| 110 origin.send(st.reply(stanza):query("http://jabber.org/protocol/disco#items")); | |
| 111 return true; | |
| 112 end, -1); | |
| 113 | |
| 114 module:hook("iq-get/host/http://jabber.org/protocol/bytestreams:query", function(event) | |
| 115 local origin, stanza = event.origin, event.stanza; | |
| 116 | |
| 117 -- check ACL | |
| 118 while proxy_acl and #proxy_acl > 0 do -- using 'while' instead of 'if' so we can break out of it | |
| 119 local jid = stanza.attr.from; | |
| 120 for _, acl in ipairs(proxy_acl) do | |
| 121 if jid_compare(jid, acl) then break; end | |
| 122 end | |
| 123 module:log("warn", "Denying use of proxy for %s", tostring(stanza.attr.from)); | |
| 124 origin.send(st.error_reply(stanza, "auth", "forbidden")); | |
| 125 return true; | |
| 129 end | 126 end |
| 130 module:log("warn", "Denying use of proxy for %s", tostring(stanza.attr.from)); | 127 |
| 131 origin.send(st.error_reply(stanza, "auth", "forbidden")); | 128 local sid = stanza.tags[1].attr.sid; |
| 129 origin.send(st.reply(stanza):tag("query", {xmlns="http://jabber.org/protocol/bytestreams", sid=sid}) | |
| 130 :tag("streamhost", {jid=host, host=proxy_address, port=proxy_port})); | |
| 132 return true; | 131 return true; |
| 133 end | 132 end); |
| 134 | 133 |
| 135 local sid = stanza.tags[1].attr.sid; | 134 module:hook("iq-set/host/http://jabber.org/protocol/bytestreams:query", function(event) |
| 136 origin.send(st.reply(stanza):tag("query", {xmlns="http://jabber.org/protocol/bytestreams", sid=sid}) | 135 local origin, stanza = event.origin, event.stanza; |
| 137 :tag("streamhost", {jid=host, host=proxy_address, port=proxy_port})); | 136 |
| 138 return true; | 137 local query = stanza.tags[1]; |
| 139 end); | 138 local sid = query.attr.sid; |
| 140 | 139 local from = stanza.attr.from; |
| 141 module.unload = function() | 140 local to = query:get_child_text("activate"); |
| 142 connlisteners.deregister(module.host .. ':proxy65'); | 141 local prepped_to = jid_prep(to); |
| 142 | |
| 143 local info = "sid: "..tostring(sid)..", initiator: "..tostring(from)..", target: "..tostring(prepped_to or to); | |
| 144 if prepped_to and sid then | |
| 145 local sha = sha1(sid .. from .. prepped_to, true); | |
| 146 if not transfers[sha] then | |
| 147 module:log("debug", "Activation request has unknown session id; activation failed (%s)", info); | |
| 148 origin.send(st.error_reply(stanza, "modify", "item-not-found")); | |
| 149 elseif not transfers[sha].initiator then | |
| 150 module:log("debug", "The sender was not connected to the proxy; activation failed (%s)", info); | |
| 151 origin.send(st.error_reply(stanza, "cancel", "not-allowed", "The sender (you) is not connected to the proxy")); | |
| 152 --elseif not transfers[sha].target then -- can't happen, as target is set when a transfer object is created | |
| 153 -- module:log("debug", "The recipient was not connected to the proxy; activation failed (%s)", info); | |
| 154 -- origin.send(st.error_reply(stanza, "cancel", "not-allowed", "The recipient is not connected to the proxy")); | |
| 155 else -- if transfers[sha].initiator ~= nil and transfers[sha].target ~= nil then | |
| 156 module:log("debug", "Transfer activated (%s)", info); | |
| 157 transfers[sha].activated = true; | |
| 158 transfers[sha].target:resume(); | |
| 159 transfers[sha].initiator:resume(); | |
| 160 origin.send(st.reply(stanza)); | |
| 161 end | |
| 162 elseif to and sid then | |
| 163 module:log("debug", "Malformed activation jid; activation failed (%s)", info); | |
| 164 origin.send(st.error_reply(stanza, "modify", "jid-malformed")); | |
| 165 else | |
| 166 module:log("debug", "Bad request; activation failed (%s)", info); | |
| 167 origin.send(st.error_reply(stanza, "modify", "bad-request")); | |
| 168 end | |
| 169 return true; | |
| 170 end); | |
| 143 end | 171 end |
| 144 | 172 |
| 145 module:hook("iq-set/host/http://jabber.org/protocol/bytestreams:query", function(event) | 173 module:provides("net", { |
| 146 local origin, stanza = event.origin, event.stanza; | 174 default_port = 5000; |
| 147 | 175 listener = listener; |
| 148 local query = stanza.tags[1]; | 176 }); |
| 149 local sid = query.attr.sid; | |
| 150 local from = stanza.attr.from; | |
| 151 local to = query:get_child_text("activate"); | |
| 152 local prepped_to = jid_prep(to); | |
| 153 | |
| 154 local info = "sid: "..tostring(sid)..", initiator: "..tostring(from)..", target: "..tostring(prepped_to or to); | |
| 155 if prepped_to and sid then | |
| 156 local sha = sha1(sid .. from .. prepped_to, true); | |
| 157 if not transfers[sha] then | |
| 158 module:log("debug", "Activation request has unknown session id; activation failed (%s)", info); | |
| 159 origin.send(st.error_reply(stanza, "modify", "item-not-found")); | |
| 160 elseif not transfers[sha].initiator then | |
| 161 module:log("debug", "The sender was not connected to the proxy; activation failed (%s)", info); | |
| 162 origin.send(st.error_reply(stanza, "cancel", "not-allowed", "The sender (you) is not connected to the proxy")); | |
| 163 --elseif not transfers[sha].target then -- can't happen, as target is set when a transfer object is created | |
| 164 -- module:log("debug", "The recipient was not connected to the proxy; activation failed (%s)", info); | |
| 165 -- origin.send(st.error_reply(stanza, "cancel", "not-allowed", "The recipient is not connected to the proxy")); | |
| 166 else -- if transfers[sha].initiator ~= nil and transfers[sha].target ~= nil then | |
| 167 module:log("debug", "Transfer activated (%s)", info); | |
| 168 transfers[sha].activated = true; | |
| 169 transfers[sha].target:resume(); | |
| 170 transfers[sha].initiator:resume(); | |
| 171 origin.send(st.reply(stanza)); | |
| 172 end | |
| 173 elseif to and sid then | |
| 174 module:log("debug", "Malformed activation jid; activation failed (%s)", info); | |
| 175 origin.send(st.error_reply(stanza, "modify", "jid-malformed")); | |
| 176 else | |
| 177 module:log("debug", "Bad request; activation failed (%s)", info); | |
| 178 origin.send(st.error_reply(stanza, "modify", "bad-request")); | |
| 179 end | |
| 180 return true; | |
| 181 end); | |
| 182 | |
| 183 if not connlisteners.register(module.host .. ':proxy65', connlistener) then | |
| 184 module:log("error", "mod_proxy65: Could not establish a connection listener. Check your configuration please."); | |
| 185 module:log("error", "Possibly two proxy65 components are configured to share the same port."); | |
| 186 end | |
| 187 | |
| 188 connlisteners.start(module.host .. ':proxy65'); |