mod_s2s_auth_dane: Disable now redundant validation done in trunk
Outgoing connections can now be validated natively in trunk since
a38f9e09ca31 so we only need to check incoming connections.
local prosody = prosody;
local core_process_stanza = prosody.core_process_stanza;
local addclient = require "net.server".addclient;
local s2s_new_outgoing = require "core.s2smanager".new_outgoing;
local initialize_filters = require "util.filters".initialize;
local st = require "util.stanza";
local portmanager = require "core.portmanager";
local softreq = require "util.dependencies".softreq;
local bit;
pcall(function() bit = require"bit"; end);
bit = bit or softreq"bit32"
if not bit then module:log("error", "No bit module found. Either LuaJIT 2, lua-bitop or Lua 5.2 is required"); end
local band = bit.band;
local rshift = bit.rshift;
local lshift = bit.lshift;
local byte = string.byte;
local c = string.char;
module:depends("s2s");
local proxy_ip = module:get_option_string("onions_socks5_host", "127.0.0.1");
local proxy_port = module:get_option_number("onions_socks5_port", 9050);
local forbid_else = module:get_option_boolean("onions_only", false);
local torify_all = module:get_option_boolean("onions_tor_all", false);
local onions_map = module:get_option("onions_map", {});
local sessions = module:shared("sessions");
-- The socks5listener handles connection while still connecting to the proxy,
-- then it hands them over to the normal listener (in mod_s2s)
local socks5listener = { default_port = proxy_port, default_mode = "*a", default_interface = "*" };
local function socks5_connect_sent(conn, data)
local session = sessions[conn];
if #data < 5 then
session.socks5_buffer = data;
return;
end
local request_status = byte(data, 2);
if not request_status == 0x00 then
module:log("debug", "Failed to connect to the SOCKS5 proxy. :(");
session:close(false);
return;
end
module:log("debug", "Successfully connected to SOCKS5 proxy.");
local response = byte(data, 4);
if response == 0x01 then
if #data < 10 then
-- let's try again when we have enough
session.socks5_buffer = data;
return;
end
-- this means the server tells us to connect on an IPv4 address
local ip = string.format("%d.%d.%d.%d", byte(data, 5,8));
local port = band(byte(data, 9), lshift(byte(data, 10), 8));
module:log("debug", "Should connect to: %s:%d", ip, port);
if not (ip == "0.0.0.0" and port == 0) then
module:log("debug", "The SOCKS5 proxy tells us to connect to a different IP, don't know how. :(");
session:close(false);
return;
end
-- Now the real s2s listener can take over the connection.
local listener = portmanager.get_service("s2s").listener;
module:log("debug", "SOCKS5 done, handing over listening to "..tostring(listener));
session.socks5_handler = nil;
session.socks5_buffer = nil;
local w, log = conn.send, session.log;
local filter = initialize_filters(session);
session.version = 1;
session.sends2s = function (t)
log("debug", "sending (s2s over socks5): %s", (t.top_tag and t:top_tag()) or t:match("^[^>]*>?"));
if t.name then
t = filter("stanzas/out", t);
end
if t then
t = filter("bytes/out", tostring(t));
if t then
return conn:write(tostring(t));
end
end
end
session.open_stream = function ()
session.sends2s(st.stanza("stream:stream", {
xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback',
["xmlns:stream"]='http://etherx.jabber.org/streams',
from=session.from_host, to=session.to_host, version='1.0', ["xml:lang"]='en'}):top_tag());
end
conn.setlistener(conn, listener);
listener.register_outgoing(conn, session);
listener.onconnect(conn);
end
end
local function socks5_handshake_sent(conn, data)
local session = sessions[conn];
if #data < 2 then
session.socks5_buffer = data;
return;
end
-- version, method
local request_status = byte(data, 2);
module:log("debug", "SOCKS version: "..byte(data, 1));
module:log("debug", "Response: "..request_status);
if not request_status == 0x00 then
module:log("debug", "Failed to connect to the SOCKS5 proxy. :( It seems to require authentication.");
session:close(false);
return;
end
module:log("debug", "Sending connect message.");
-- version 5, connect, (reserved), type: domainname, (length, hostname), port
conn:write(c(5) .. c(1) .. c(0) .. c(3) .. c(#session.socks5_to) .. session.socks5_to);
conn:write(c(rshift(session.socks5_port, 8)) .. c(band(session.socks5_port, 0xff)));
session.socks5_handler = socks5_connect_sent;
end
function socks5listener.onconnect(conn)
module:log("debug", "Connected to SOCKS5 proxy, sending SOCKS5 handshake.");
-- Socks version 5, 1 method, no auth
conn:write(c(5) .. c(1) .. c(0));
sessions[conn].socks5_handler = socks5_handshake_sent;
end
function socks5listener.register_outgoing(conn, session)
session.direction = "outgoing";
sessions[conn] = session;
end
function socks5listener.ondisconnect(conn, err)
sessions[conn] = nil;
end
function socks5listener.onincoming(conn, data)
local session = sessions[conn];
if session.socks5_buffer then
data = session.socks5_buffer .. data;
end
if session.socks5_handler then
session.socks5_handler(conn, data);
end
end
local function connect_socks5(host_session, connect_host, connect_port)
module:log("debug", "Connecting to " .. connect_host .. ":" .. connect_port);
-- this is not necessarily the same as .to_host (it can be that this is from the onions_map)
host_session.socks5_to = connect_host;
host_session.socks5_port = connect_port;
local conn = addclient(proxy_ip, proxy_port, socks5listener, "*a");
socks5listener.register_outgoing(conn, host_session);
host_session.conn = conn;
end
local bouncy_stanzas = { message = true, presence = true, iq = true };
local function bounce_sendq(session, reason)
local sendq = session.sendq;
if not sendq then return; end
session.log("info", "Sending error replies for "..#sendq.." queued stanzas because of failed outgoing connection to "..tostring(session.to_host));
local dummy = {
type = "s2sin";
send = function(s)
(session.log or log)("error", "Replying to to an s2s error reply, please report this! Traceback: %s", debug.traceback());
end;
dummy = true;
};
for i, data in ipairs(sendq) do
local reply = data[2];
if reply and not(reply.attr.xmlns) and bouncy_stanzas[reply.name] then
reply.attr.type = "error";
reply:tag("error", {type = "cancel"})
:tag("remote-server-not-found", {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):up();
if reason then
reply:tag("text", {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"})
:text("Server-to-server connection failed: "..reason):up();
end
core_process_stanza(dummy, reply);
end
sendq[i] = nil;
end
session.sendq = nil;
end
-- Try to intercept anything to *.onion
local function route_to_onion(event)
local stanza = event.stanza;
local to_host = event.to_host;
local onion_host = nil;
local onion_port = nil;
if not to_host:find("%.onion$") then
if onions_map[to_host] then
if type(onions_map[to_host]) == "string" then
onion_host = onions_map[to_host];
else
onion_host = onions_map[to_host].host;
onion_port = onions_map[to_host].port;
end
elseif forbid_else then
module:log("debug", event.to_host .. " is not an onion. Blocking it.");
return false;
elseif not torify_all then
return;
end
end
module:log("debug", "Onion routing something to ".. to_host);
if hosts[event.from_host].s2sout[to_host] then
return;
end
local host_session = s2s_new_outgoing(event.from_host, to_host);
host_session.bounce_sendq = bounce_sendq;
host_session.sendq = { {tostring(stanza), stanza.attr and stanza.attr.type ~= "error" and stanza.attr.type ~= "result" and st.reply(stanza)} };
hosts[event.from_host].s2sout[to_host] = host_session;
connect_socks5(host_session, onion_host or to_host, onion_port or 5269);
return true;
end
module:log("debug", "Onions ready and loaded");
module:hook("route/remote", route_to_onion, 200);
module:hook_global("s2s-check-certificate", function (event)
local host = event.host;
if host and host:find("%.onion$") then
return true;
end
end);