s2smanager: Give reasons to destroy_session in more cases, including when DNS lookup...
[prosody.git] / core / s2smanager.lua
index 3613707cf1a962712821991bc452ca72afc95609..f444e8437ca64b54042ff3030c32785e825ad9df 100644 (file)
@@ -50,9 +50,11 @@ local incoming_s2s = incoming_s2s;
 
 module "s2smanager"
 
-local function compare_srv_priorities(a,b) return a.priority < b.priority or a.weight < b.weight; end
+function compare_srv_priorities(a,b)
+       return a.priority < b.priority or (a.priority == b.priority and a.weight > b.weight);
+end
 
-local function bounce_sendq(session)
+local function bounce_sendq(session, reason)
        local sendq = session.sendq;
        if sendq then
                session.log("info", "sending error replies for "..#sendq.." queued stanzas because of failed outgoing connection to "..tostring(session.to_host));
@@ -70,6 +72,9 @@ local function bounce_sendq(session)
                                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("Connection failed: "..reason):up();
+                               end
                                core_process_stanza(dummy, reply);
                        end
                        sendq[i] = nil;
@@ -79,15 +84,15 @@ local function bounce_sendq(session)
 end
 
 function send_to_host(from_host, to_host, data)
+       if not hosts[from_host] then
+               log("warn", "Attempt to send stanza from %s - a host we don't serve", from_host);
+               return false;
+       end
        local host = hosts[from_host].s2sout[to_host];
        if host then
                -- We have a connection to this host already
-               if host.type == "s2sout_unauthed" and data.name ~= "db:verify" and ((not data.xmlns) or data.xmlns == "jabber:client" or data.xmlns == "jabber:server") then
+               if host.type == "s2sout_unauthed" and (data.name ~= "db:verify" or not host.dialback_key) and ((not data.xmlns) or data.xmlns == "jabber:client" or data.xmlns == "jabber:server") then
                        (host.log or log)("debug", "trying to send over unauthed s2sout to "..to_host);
-                       if not host.notopen and not host.dialback_key and host.sends2s then
-                               host.log("debug", "dialback had not been initiated");
-                               initiate_dialback(host);
-                       end
                        
                        -- Queue stanza until we are able to send it
                        if host.sendq then t_insert(host.sendq, {tostring(data), st.reply(data)});
@@ -110,6 +115,7 @@ function send_to_host(from_host, to_host, data)
        else
                log("debug", "opening a new outgoing connection for this stanza");
                local host_session = new_outgoing(from_host, to_host);
+
                -- Store in buffer
                host_session.sendq = { {tostring(data), st.reply(data)} };
                log("debug", "stanza [%s] queued until connection complete", tostring(data.name));
@@ -159,7 +165,7 @@ function new_outgoing(from_host, to_host)
                        host_session.log = log;
                end
                
-               -- This is the first call, can't fail (the first step is DNS lookup)
+               -- Kick the connection attempting machine
                attempt_connection(host_session);
                
                if not host_session.sends2s then                
@@ -187,6 +193,10 @@ function attempt_connection(host_session, err)
        local from_host, to_host = host_session.from_host, host_session.to_host;
        local connect_host, connect_port = idna_to_ascii(to_host), 5269;
        
+       if not connect_host then
+               return false;
+       end
+       
        if not err then -- This is our first attempt
                log("debug", "First attempt to connect to %s, starting with SRV lookup...", to_host);
                host_session.connecting = true;
@@ -217,7 +227,7 @@ function attempt_connection(host_session, err)
                        if not ok then
                                if not attempt_connection(host_session, err) then
                                        -- No more attempts will be made
-                                       destroy_session(host_session);
+                                       destroy_session(host_session, err);
                                end
                        end
                end, "_xmpp-server._tcp."..connect_host..".", "SRV");
@@ -277,7 +287,7 @@ function try_connect(host_session, connect_host, connect_port)
                        log("debug", "DNS lookup failed to get a response for %s", connect_host);
                        if not attempt_connection(host_session, "name resolution failed") then -- Retry if we can
                                log("debug", "No other records to try for %s - destroying", host_session.to_host);
-                               destroy_session(host_session); -- End of the line, we can't
+                               destroy_session(host_session, "DNS resolution failed"); -- End of the line, we can't
                        end
                end
        end, connect_host, "A", "IN");
@@ -299,6 +309,11 @@ function make_connect(host_session, connect_host, connect_port)
        local from_host, to_host = host_session.from_host, host_session.to_host;
        
        local conn, handler = socket.tcp()
+       
+       if not conn then
+               log("warn", "Failed to create outgoing connection, system error: %s", handler);
+               return false, handler;
+       end
 
        conn:settimeout(0);
        local success, err = conn:connect(connect_host, connect_port);
@@ -357,17 +372,17 @@ function streamopened(session, attr)
        
                session.streamid = uuid_gen();
                (session.log or log)("debug", "incoming s2s received <stream:stream>");
-               send("<?xml version='1.0'?>");
-               send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', 
-                               ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host, version=(session.version > 0 and "1.0" or nil) }):top_tag());
                if session.to_host and not hosts[session.to_host] then
                        -- Attempting to connect to a host we don't serve
                        session:close({ condition = "host-unknown"; text = "This host does not serve "..session.to_host });
                        return;
                end
+               send("<?xml version='1.0'?>");
+               send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', 
+                               ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host, version=(session.version > 0 and "1.0" or nil) }):top_tag());
                if session.version >= 1.0 then
                        local features = st.stanza("stream:features");
-                                                       
+                       
                        if session.to_host then
                                hosts[session.to_host].events.fire_event("s2s-stream-features", { session = session, features = features });
                        else
@@ -390,7 +405,7 @@ function streamopened(session, attr)
                if send_buffer and #send_buffer > 0 then
                        log("debug", "Sending s2s send_buffer now...");
                        for i, data in ipairs(send_buffer) do
-                               session.sends2s(tostring(data));
+                               session.sends2s(data);
                                send_buffer[i] = nil;
                        end
                end
@@ -479,12 +494,12 @@ function mark_connected(session)
        end
 end
 
-function destroy_session(session)
+function destroy_session(session, reason)
        (session.log or log)("info", "Destroying "..tostring(session.direction).." session "..tostring(session.from_host).."->"..tostring(session.to_host));
        
        if session.direction == "outgoing" then
                hosts[session.from_host].s2sout[session.to_host] = nil;
-               bounce_sendq(session);
+               bounce_sendq(session, reason);
        elseif session.direction == "incoming" then
                incoming_s2s[session] = nil;
        end