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