2 -- Copyright (C) 2008-2011 Matthew Wild
3 -- Copyright (C) 2008-2011 Waqas Hussain
4 -- Copyright (C) 2009 Thilo Cestonaro
6 -- This project is MIT/X11 licensed. Please see the
7 -- COPYING file in the source package for more information.
12 local jid_compare
, jid_prep
= require
"util.jid".compare
, require
"util.jid".prep
;
13 local st
= require
"util.stanza";
14 local sha1
= require
"util.hashes".sha1
;
15 local b64
= require
"util.encodings".base64
.encode
;
16 local server
= require
"net.server";
17 local portmanager
= require
"core.portmanager";
19 local sessions
= module
:shared("sessions");
20 local transfers
= module
:shared("transfers");
21 local max_buffer_size
= 4096;
25 function listener
.onincoming(conn
, data
)
26 local session
= sessions
[conn
] or {};
28 local transfer
= transfers
[session
.sha
];
29 if transfer
and transfer
.activated
then -- copy data between initiator and target
30 local initiator
, target
= transfer
.initiator
, transfer
.target
;
31 (conn
== initiator
and target
or initiator
):write(data
);
33 end -- FIXME server.link should be doing this?
35 if not session
.greeting_done
then
36 local nmethods
= data
:byte(2) or 0;
37 if data
:byte(1) == 0x05 and nmethods
> 0 and #data
== 2 + nmethods
then -- check if we have all the data
38 if data
:find("%z") then -- 0x00 = 'No authentication' is supported
39 session
.greeting_done
= true;
40 sessions
[conn
] = session
;
41 conn
:write("\5\0"); -- send (SOCKS version 5, No authentication)
42 module
:log("debug", "SOCKS5 greeting complete");
45 end -- else error, unexpected input
46 conn
:write("\5\255"); -- send (SOCKS version 5, no acceptable method)
48 module
:log("debug", "Invalid SOCKS5 greeting received: '%s'", b64(data
));
49 else -- connection request
50 --local head = string.char( 0x05, 0x01, 0x00, 0x03, 40 ); -- ( VER=5=SOCKS5, CMD=1=CONNECT, RSV=0=RESERVED, ATYP=3=DOMAIMNAME, SHA-1 size )
51 if #data
== 47 and data
:sub(1,5) == "\5\1\0\3\40" and data
:sub(-2) == "\0\0" then
52 local sha
= data
:sub(6, 45);
54 conn
:write("\5\0\0\3\40" .. sha
.. "\0\0"); -- VER, REP, RSV, ATYP, BND.ADDR (sha), BND.PORT (2 Byte)
55 if not transfers
[sha
] then
57 transfers
[sha
].target
= conn
;
59 module
:log("debug", "SOCKS5 target connected for session %s", sha
);
60 else -- transfers[sha].target ~= nil
61 transfers
[sha
].initiator
= conn
;
63 module
:log("debug", "SOCKS5 initiator connected for session %s", sha
);
64 server
.link(conn
, transfers
[sha
].target
, max_buffer_size
);
65 server
.link(transfers
[sha
].target
, conn
, max_buffer_size
);
67 else -- error, unexpected input
68 conn
:write("\5\1\0\3\0\0\0"); -- VER, REP, RSV, ATYP, BND.ADDR (sha), BND.PORT (2 Byte)
70 module
:log("debug", "Invalid SOCKS5 negotiation received: '%s'", b64(data
));
75 function listener
.ondisconnect(conn
)
76 local session
= sessions
[conn
];
78 if transfers
[session
.sha
] then
79 local initiator
, target
= transfers
[session
.sha
].initiator
, transfers
[session
.sha
].target
;
80 if initiator
== conn
and target
~= nil then
82 elseif target
== conn
and initiator
~= nil then
85 transfers
[session
.sha
] = nil;
87 -- Clean up any session-related stuff here
92 function module
.add_host(module
)
93 local host
, name
= module
:get_host(), module
:get_option_string("name", "SOCKS5 Bytestreams Service");
95 local proxy_address
= module
:get_option_string("proxy65_address", host
);
96 local proxy_acl
= module
:get_option_array("proxy65_acl");
98 -- COMPAT w/pre-0.9 where proxy65_port was specified in the components section of the config
99 local legacy_config
= module
:get_option_number("proxy65_port");
100 if legacy_config
then
101 module
:log("warn", "proxy65_port is deprecated, please put proxy65_ports = { %d } into the global section instead", legacy_config
);
104 module
:depends("disco");
105 module
:add_identity("proxy", "bytestreams", name
);
106 module
:add_feature("http://jabber.org/protocol/bytestreams");
108 module
:hook("iq-get/host/http://jabber.org/protocol/bytestreams:query", function(event
)
109 local origin
, stanza
= event
.origin
, event
.stanza
;
112 -- using 'while' instead of 'if' so we can break out of it
113 while proxy_acl
and #proxy_acl
> 0 do --luacheck: ignore 512
114 local jid
= stanza
.attr
.from
;
116 for _
, acl
in ipairs(proxy_acl
) do
117 if jid_compare(jid
, acl
) then allow
= true; break; end
119 if allow
then break; end
120 module
:log("warn", "Denying use of proxy for %s", tostring(stanza
.attr
.from
));
121 origin
.send(st
.error_reply(stanza
, "auth", "forbidden"));
125 local proxy_port
= next(portmanager
.get_active_services():search("proxy65", nil)[1] or {});
126 if not proxy_port
then
127 module
:log("warn", "Not listening on any port");
128 origin
.send(st
.error_reply(stanza
, "wait", "item-not-found", "Not listening on any port"));
132 local sid
= stanza
.tags
[1].attr
.sid
;
133 origin
.send(st
.reply(stanza
):tag("query", {xmlns
="http://jabber.org/protocol/bytestreams", sid
=sid
})
134 :tag("streamhost", {jid
=host
, host
=proxy_address
, port
=("%d"):format(proxy_port
)}));
138 module
:hook("iq-set/host/http://jabber.org/protocol/bytestreams:query", function(event
)
139 local origin
, stanza
= event
.origin
, event
.stanza
;
141 local query
= stanza
.tags
[1];
142 local sid
= query
.attr
.sid
;
143 local from
= stanza
.attr
.from
;
144 local to
= query
:get_child_text("activate");
145 local prepped_to
= jid_prep(to
);
147 local info
= "sid: "..tostring(sid
)..", initiator: "..tostring(from
)..", target: "..tostring(prepped_to
or to
);
148 if prepped_to
and sid
then
149 local sha
= sha1(sid
.. from
.. prepped_to
, true);
150 if not transfers
[sha
] then
151 module
:log("debug", "Activation request has unknown session id; activation failed (%s)", info
);
152 origin
.send(st
.error_reply(stanza
, "modify", "item-not-found"));
153 elseif not transfers
[sha
].initiator
then
154 module
:log("debug", "The sender was not connected to the proxy; activation failed (%s)", info
);
155 origin
.send(st
.error_reply(stanza
, "cancel", "not-allowed", "The sender (you) is not connected to the proxy"));
156 --elseif not transfers[sha].target then -- can't happen, as target is set when a transfer object is created
157 -- module:log("debug", "The recipient was not connected to the proxy; activation failed (%s)", info);
158 -- origin.send(st.error_reply(stanza, "cancel", "not-allowed", "The recipient is not connected to the proxy"));
159 else -- if transfers[sha].initiator ~= nil and transfers[sha].target ~= nil then
160 module
:log("debug", "Transfer activated (%s)", info
);
161 transfers
[sha
].activated
= true;
162 transfers
[sha
].target
:resume();
163 transfers
[sha
].initiator
:resume();
164 origin
.send(st
.reply(stanza
));
166 elseif to
and sid
then
167 module
:log("debug", "Malformed activation jid; activation failed (%s)", info
);
168 origin
.send(st
.error_reply(stanza
, "modify", "jid-malformed"));
170 module
:log("debug", "Bad request; activation failed (%s)", info
);
171 origin
.send(st
.error_reply(stanza
, "modify", "bad-request"));
177 module
:provides("net", {