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; |
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'); |
|