Comparison

plugins/mod_proxy65.lua @ 4414:aa2e79f20962

mod_proxy65: Major cleanup, better logging, handling of all error cases, less code, and other goodness.
author Waqas Hussain <waqas20@gmail.com>
date Sun, 06 Nov 2011 00:51:39 +0500
parent 4376:99277a1abe58
child 4679:5b52b5eaa03d
comparison
equal deleted inserted replaced
4413:ffa4bed1b716 4414:aa2e79f20962
1 -- Prosody IM
2 -- Copyright (C) 2008-2011 Matthew Wild
3 -- Copyright (C) 2008-2011 Waqas Hussain
1 -- Copyright (C) 2009 Thilo Cestonaro 4 -- Copyright (C) 2009 Thilo Cestonaro
2 -- 5 --
3 -- This project is MIT/X11 licensed. Please see the 6 -- This project is MIT/X11 licensed. Please see the
4 -- COPYING file in the source package for more information. 7 -- COPYING file in the source package for more information.
5 -- 8 --
16 local jid_compare, jid_prep = require "util.jid".compare, require "util.jid".prep; 19 local jid_compare, jid_prep = require "util.jid".compare, require "util.jid".prep;
17 local st = require "util.stanza"; 20 local st = require "util.stanza";
18 local connlisteners = require "net.connlisteners"; 21 local connlisteners = require "net.connlisteners";
19 local sha1 = require "util.hashes".sha1; 22 local sha1 = require "util.hashes".sha1;
20 local server = require "net.server"; 23 local server = require "net.server";
24 local b64 = require "util.encodings".base64.encode;
21 25
22 local host, name = module:get_host(), "SOCKS5 Bytestreams Service"; 26 local host, name = module:get_host(), "SOCKS5 Bytestreams Service";
23 local sessions, transfers = {}, {}; 27 local sessions, transfers = {}, {};
24 28
25 local proxy_port = module:get_option("proxy65_port") or 5000; 29 local proxy_port = module:get_option("proxy65_port") or 5000;
30 34
31 local connlistener = { default_port = proxy_port, default_interface = proxy_interface, default_mode = "*a" }; 35 local connlistener = { default_port = proxy_port, default_interface = proxy_interface, default_mode = "*a" };
32 36
33 function connlistener.onincoming(conn, data) 37 function connlistener.onincoming(conn, data)
34 local session = sessions[conn] or {}; 38 local session = sessions[conn] or {};
39
40 local transfer = transfers[session.sha];
41 if transfer and transfer.activated then -- copy data between initiator and target
42 local initiator, target = transfer.initiator, transfer.target;
43 (conn == initiator and target or initiator):write(data);
44 return;
45 end -- FIXME server.link should be doing this?
35 46
36 if session.setup == nil and data ~= nil and data:byte(1) == 0x05 and #data > 2 then 47 if not session.greeting_done then
37 local nmethods = data:byte(2); 48 local nmethods = data:byte(2) or 0;
38 local methods = data:sub(3); 49 if data:byte(1) == 0x05 and nmethods > 0 and #data == 2 + nmethods then -- check if we have all the data
39 local supported = false; 50 if data:find("%z") then -- 0x00 = 'No authentication' is supported
40 for i=1, nmethods, 1 do 51 session.greeting_done = true;
41 if(methods:byte(i) == 0x00) then -- 0x00 == method: NO AUTH 52 sessions[conn] = session;
42 supported = true; 53 conn:write("\5\0"); -- send (SOCKS version 5, No authentication)
43 break; 54 module:log("debug", "SOCKS5 greeting complete");
44 end
45 end
46 if(supported) then
47 module:log("debug", "new session found ... ")
48 session.setup = true;
49 sessions[conn] = session;
50 conn:write(string.char(5, 0));
51 end
52 return;
53 end
54 if session.setup then
55 if session.sha ~= nil and transfers[session.sha] ~= nil then
56 local sha = session.sha;
57 if transfers[sha].activated == true and transfers[sha].target ~= nil then
58 if transfers[sha].initiator == conn then
59 transfers[sha].target:write(data);
60 else
61 transfers[sha].initiator:write(data);
62 end
63 return; 55 return;
64 end 56 end
65 end 57 end -- else error, unexpected input
66 if data ~= nil and #data == 0x2F and -- 40 == length of SHA1 HASH, and 7 other bytes => 47 => 0x2F 58 conn:write("\5\255"); -- send (SOCKS version 5, no acceptable method)
67 data:byte(1) == 0x05 and -- SOCKS5 has 5 in first byte 59 conn:close();
68 data:byte(2) == 0x01 and -- CMD must be 1 60 module:log("debug", "Invalid SOCKS5 greeting recieved: '%s'", b64(data));
69 data:byte(3) == 0x00 and -- RSV must be 0 61 else -- connection request
70 data:byte(4) == 0x03 and -- ATYP must be 3 62 --local head = string.char( 0x05, 0x01, 0x00, 0x03, 40 ); -- ( VER=5=SOCKS5, CMD=1=CONNECT, RSV=0=RESERVED, ATYP=3=DOMAIMNAME, SHA-1 size )
71 data:byte(5) == 40 and -- SHA1 HASH length must be 40 (0x28) 63 if #data == 47 and data:sub(1,5) == "\5\1\0\3\40" and data:sub(-2) == "\0\0" then
72 data:byte(-2) == 0x00 and -- PORT must be 0, size 2 byte 64 local sha = data:sub(6, 45);
73 data:byte(-1) == 0x00 65 conn:pause();
74 then 66 conn:write("\5\0\0\3\40" .. sha .. "\0\0"); -- VER, REP, RSV, ATYP, BND.ADDR (sha), BND.PORT (2 Byte)
75 local sha = data:sub(6, 45); -- second param is not count! it's the ending index (included!) 67 if not transfers[sha] then
76 if transfers[sha] == nil then
77 transfers[sha] = {}; 68 transfers[sha] = {};
78 transfers[sha].activated = false;
79 transfers[sha].target = conn; 69 transfers[sha].target = conn;
80 session.sha = sha; 70 session.sha = sha;
81 module:log("debug", "target connected ... "); 71 module:log("debug", "SOCKS5 target connected for session %s", sha);
82 elseif transfers[sha].target ~= nil then 72 else -- transfers[sha].target ~= nil
83 transfers[sha].initiator = conn; 73 transfers[sha].initiator = conn;
84 session.sha = sha; 74 session.sha = sha;
85 module:log("debug", "initiator connected ... "); 75 module:log("debug", "SOCKS5 initiator connected for session %s", sha);
86 server.link(conn, transfers[sha].target, max_buffer_size); 76 server.link(conn, transfers[sha].target, max_buffer_size);
87 server.link(transfers[sha].target, conn, max_buffer_size); 77 server.link(transfers[sha].target, conn, max_buffer_size);
88 end 78 end
89 conn:write(string.char(5, 0, 0, 3, #sha) .. sha .. string.char(0, 0)); -- VER, REP, RSV, ATYP, BND.ADDR (sha), BND.PORT (2 Byte) 79 else -- error, unexpected input
90 conn:lock_read(true) 80 conn:write("\5\1\0\3\0\0\0"); -- VER, REP, RSV, ATYP, BND.ADDR (sha), BND.PORT (2 Byte)
91 else
92 module:log("warn", "Neither data transfer nor initial connect of a participator of a transfer.")
93 conn:close(); 81 conn:close();
94 end 82 module:log("debug", "Invalid SOCKS5 negotiation recieved: '%s'", b64(data));
95 else
96 if data ~= nil then
97 module:log("warn", "unknown connection with no authentication data -> closing it");
98 conn:close();
99 end 83 end
100 end 84 end
101 end 85 end
102 86
103 function connlistener.ondisconnect(conn, err) 87 function connlistener.ondisconnect(conn, err)
104 local session = sessions[conn]; 88 local session = sessions[conn];
105 if session then 89 if session then
106 if session.sha and transfers[session.sha] then 90 if transfers[session.sha] then
107 local initiator, target = transfers[session.sha].initiator, transfers[session.sha].target; 91 local initiator, target = transfers[session.sha].initiator, transfers[session.sha].target;
108 if initiator == conn and target ~= nil then 92 if initiator == conn and target ~= nil then
109 target:close(); 93 target:close();
110 elseif target == conn and initiator ~= nil then 94 elseif target == conn and initiator ~= nil then
111 initiator:close(); 95 initiator:close();
165 local sid = query.attr.sid; 149 local sid = query.attr.sid;
166 local from = stanza.attr.from; 150 local from = stanza.attr.from;
167 local to = query:get_child_text("activate"); 151 local to = query:get_child_text("activate");
168 local prepped_to = jid_prep(to); 152 local prepped_to = jid_prep(to);
169 153
170 module:log("debug", "received activation request from %s", stanza.attr.from); 154 local info = "sid: "..tostring(sid)..", initiator: "..tostring(from)..", target: "..tostring(prepped_to or to);
171 if prepped_to and sid then 155 if prepped_to and sid then
172 local sha = sha1(sid .. from .. prepped_to, true); 156 local sha = sha1(sid .. from .. prepped_to, true);
173 if transfers[sha] == nil then 157 if not transfers[sha] then
174 module:log("error", "transfers[sha]: nil"); 158 module:log("debug", "Activation request has unknown session id; activation failed (%s)", info);
175 origin.send(st.error_reply(stanza, "modify", "item-not-found")); 159 origin.send(st.error_reply(stanza, "modify", "item-not-found"));
176 elseif(transfers[sha] ~= nil and transfers[sha].initiator ~= nil and transfers[sha].target ~= nil) then 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);
177 transfers[sha].activated = true; 168 transfers[sha].activated = true;
178 transfers[sha].target:lock_read(false); 169 transfers[sha].target:resume();
179 transfers[sha].initiator:lock_read(false); 170 transfers[sha].initiator:resume();
180 origin.send(st.reply(stanza)); 171 origin.send(st.reply(stanza));
181 else
182 module:log("debug", "Both parties were not yet connected");
183 local message = "Neither party is connected to the proxy";
184 if transfers[sha].initiator then
185 message = "The recipient is not connected to the proxy";
186 elseif transfers[sha].target then
187 message = "The sender (you) is not connected to the proxy";
188 end
189 origin.send(st.error_reply(stanza, "cancel", "not-allowed", message));
190 end 172 end
191 elseif to and sid then 173 elseif to and sid then
174 module:log("debug", "Malformed activation jid; activation failed (%s)", info);
192 origin.send(st.error_reply(stanza, "modify", "jid-malformed")); 175 origin.send(st.error_reply(stanza, "modify", "jid-malformed"));
193 else 176 else
194 module:log("error", "activation failed: sid: %s, initiator: %s, target: %s", tostring(sid), tostring(from), tostring(to)); 177 module:log("debug", "Bad request; activation failed (%s)", info);
195 origin.send(st.error_reply(stanza, "modify", "bad-request")); 178 origin.send(st.error_reply(stanza, "modify", "bad-request"));
196 end 179 end
197 return true; 180 return true;
198 end); 181 end);
199 182