core/s2smanager.lua
branchs2s
changeset 148 4c0dcd245d34
child 157 f4e9b6ec34b0
equal deleted inserted replaced
147:ccebb2720741 148:4c0dcd245d34
       
     1 
       
     2 local hosts = hosts;
       
     3 local sessions = sessions;
       
     4 local socket = require "socket";
       
     5 local format = string.format;
       
     6 local tostring, pairs, ipairs, getmetatable, print, newproxy, error, tonumber
       
     7     = tostring, pairs, ipairs, getmetatable, print, newproxy, error, tonumber;
       
     8 
       
     9 local connlisteners_get = require "net.connlisteners".get;
       
    10 local wraptlsclient = require "net.server".wraptlsclient;
       
    11 local modulemanager = require "core.modulemanager";
       
    12 
       
    13 local uuid_gen = require "util.uuid".generate;
       
    14 
       
    15 local logger_init = require "util.logger".init;
       
    16 
       
    17 local log = logger_init("s2smanager");
       
    18 
       
    19 local md5_hash = require "util.hashes".md5;
       
    20 
       
    21 local dialback_secret = "This is very secret!!! Ha!";
       
    22 
       
    23 module "s2smanager"
       
    24 
       
    25 function connect_host(from_host, to_host)
       
    26 end
       
    27 
       
    28 function send_to_host(from_host, to_host, data)
       
    29 	if hosts[to_host] then
       
    30 		-- Write to connection
       
    31 		hosts[to_host].send(data);
       
    32 		log("debug", "stanza sent over s2s");
       
    33 	else
       
    34 		log("debug", "opening a new outgoing connection for this stanza");
       
    35 		local host_session = new_outgoing(from_host, to_host);
       
    36 		-- Store in buffer
       
    37 		host_session.sendq = { data };
       
    38 	end
       
    39 end
       
    40 
       
    41 function disconnect_host(host)
       
    42 	
       
    43 end
       
    44 
       
    45 local open_sessions = 0;
       
    46 
       
    47 function new_incoming(conn)
       
    48 	local session = { conn = conn,  priority = 0, type = "s2sin_unauthed", direction = "incoming" };
       
    49 	if true then
       
    50 		session.trace = newproxy(true);
       
    51 		getmetatable(session.trace).__gc = function () open_sessions = open_sessions - 1; print("s2s session got collected, now "..open_sessions.." s2s sessions are allocated") end;
       
    52 	end
       
    53 	open_sessions = open_sessions + 1;
       
    54 	local w = conn.write;
       
    55 	session.send = function (t) w(tostring(t)); end
       
    56 	return session;
       
    57 end
       
    58 
       
    59 function new_outgoing(from_host, to_host)
       
    60 		local host_session = { to_host = to_host, from_host = from_host, notopen = true, type = "s2sout_unauthed", direction = "outgoing" };
       
    61 		hosts[to_host] = host_session;
       
    62 
       
    63 		local cl = connlisteners_get("xmppserver");
       
    64 		
       
    65 		local conn, handler = socket.tcp()
       
    66 		--FIXME: Below parameters (ports/ip) are incorrect (use SRV)
       
    67 		conn:connect(to_host, 5269);
       
    68 		conn = wraptlsclient(cl, conn, to_host, 5269, 0, 1, hosts[from_host].ssl_ctx );
       
    69 		host_session.conn = conn;
       
    70 		
       
    71 		-- Register this outgoing connection so that xmppserver_listener knows about it
       
    72 		-- otherwise it will assume it is a new incoming connection
       
    73 		cl.register_outgoing(conn, host_session);
       
    74 		
       
    75 		do
       
    76 			local conn_name = "s2sout"..tostring(conn):match("[a-f0-9]*$");
       
    77 			host_session.log = logger_init(conn_name);
       
    78 		end
       
    79 		
       
    80 		local w = conn.write;
       
    81 		host_session.send = function (t) w(tostring(t)); end
       
    82 		
       
    83 		conn.write(format([[<stream:stream xmlns='jabber:server' xmlns:db='jabber:server:dialback' xmlns:stream='http://etherx.jabber.org/streams' from='%s' to='%s' version='1.0'>]], from_host, to_host));
       
    84 		 
       
    85 		return host_session;
       
    86 end
       
    87 
       
    88 function streamopened(session, attr)
       
    89 	session.log("debug", "s2s stream opened");
       
    90 	local send = session.send;
       
    91 	
       
    92 	session.version = tonumber(attr.version) or 0;
       
    93 	if session.version >= 1.0 and not (attr.to and attr.from) then
       
    94 		print("to: "..tostring(attr.to).." from: "..tostring(attr.from));
       
    95 		--error(session.to_host.." failed to specify 'to' or 'from' hostname as per RFC");
       
    96 		log("warn", (session.to_host or "(unknown)").." failed to specify 'to' or 'from' hostname as per RFC");
       
    97 	end
       
    98 	
       
    99 	if session.direction == "incoming" then
       
   100 		-- Send a reply stream header
       
   101 		
       
   102 		for k,v in pairs(attr) do print("", tostring(k), ":::", tostring(v)); end
       
   103 		
       
   104 		session.to_host = attr.to;
       
   105 		session.from_host = attr.from;
       
   106 	
       
   107 		session.streamid = uuid_gen();
       
   108 		print(session, session.from_host, "incoming s2s stream opened");
       
   109 		send("<?xml version='1.0'?>");
       
   110 		send(format("<stream:stream xmlns='jabber:server' xmlns:db='jabber:server:dialback' xmlns:stream='http://etherx.jabber.org/streams' id='%s' from='%s'>", session.streamid, session.to_host));
       
   111 		if session.from_host then
       
   112 			-- Need to perform dialback to check identity
       
   113 			print("to: "..tostring(attr.to).." from: "..tostring(attr.from));
       
   114 			print("Need to do dialback here you know!!");
       
   115 		end
       
   116 	elseif session.direction == "outgoing" then
       
   117 		-- If we are just using the connection for verifying dialback keys, we won't try and auth it
       
   118 		if not session.dialback_verifying then
       
   119 			-- generate dialback key
       
   120 			if not attr.id then error("stream response did not give us a streamid!!!"); end
       
   121 			session.streamid = attr.id;
       
   122 			session.dialback_key = generate_dialback(session.streamid, session.to_host, session.from_host);
       
   123 			session.send(format("<db:result from='%s' to='%s'>%s</db:result>", session.from_host, session.to_host, session.dialback_key));
       
   124 			session.log("info", "sent dialback key on outgoing s2s stream");
       
   125 		else
       
   126 			mark_connected(session);
       
   127 		end
       
   128 	end
       
   129 	--[[
       
   130 	local features = {};
       
   131 	modulemanager.fire_event("stream-features-s2s", session, features);
       
   132 	
       
   133 	send("<stream:features>");
       
   134 	
       
   135 	for _, feature in ipairs(features) do
       
   136 		send(tostring(feature));
       
   137 	end
       
   138 
       
   139 	send("</stream:features>");]]
       
   140 	log("info", "s2s stream opened successfully");
       
   141 	session.notopen = nil;
       
   142 end
       
   143 
       
   144 function generate_dialback(id, to, from)
       
   145 	return md5_hash(id..to..from..dialback_secret); -- FIXME: See XEP-185 and XEP-220
       
   146 end
       
   147 
       
   148 function verify_dialback(id, to, from, key)
       
   149 	return key == generate_dialback(id, to, from);
       
   150 end
       
   151 
       
   152 function make_authenticated(session)
       
   153 	if session.type == "s2sout_unauthed" then
       
   154 		session.type = "s2sout";
       
   155 	elseif session.type == "s2sin_unauthed" then
       
   156 		session.type = "s2sin";
       
   157 	else
       
   158 		return false;
       
   159 	end
       
   160 	session.log("info", "connection is now authenticated");
       
   161 	
       
   162 	mark_connected(session);
       
   163 	
       
   164 	return true;
       
   165 end
       
   166 
       
   167 function mark_connected(session)
       
   168 	local sendq, send = session.sendq, session.send;
       
   169 	if sendq then
       
   170 		session.log("debug", "sending queued stanzas across new connection");
       
   171 		for _, data in ipairs(sendq) do
       
   172 			session.log("debug", "sending: %s", tostring(data));
       
   173 			send(data);
       
   174 		end
       
   175 	end
       
   176 end
       
   177 
       
   178 return _M;