Commit 026c3f2f authored by Dave Cridland's avatar Dave Cridland

Support dialback errors

See XEP-0220, Dialback Errors.

This reduces disconnect in the case of piggybacking errors, and provides better
diagnostics.
parent d02e7f9e
...@@ -61,6 +61,7 @@ import org.xmlpull.v1.XmlPullParser; ...@@ -61,6 +61,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlPullParserFactory;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.StreamError; import org.xmpp.packet.StreamError;
/** /**
...@@ -85,8 +86,13 @@ import org.xmpp.packet.StreamError; ...@@ -85,8 +86,13 @@ import org.xmpp.packet.StreamError;
* @author Gaston Dombiak * @author Gaston Dombiak
*/ */
public class ServerDialback { public class ServerDialback {
private enum VerifyResult {
private static final Logger Log = LoggerFactory.getLogger(ServerDialback.class); decline, // For some reason, we declined to do the verify.
error, // Remote error from the authoritative server.
valid, // Explicitly valid.
invalid // Invalid.
}
private static final Logger Log = LoggerFactory.getLogger(ServerDialback.class);
/** /**
* The utf-8 charset for decoding and encoding Jabber packet streams. * The utf-8 charset for decoding and encoding Jabber packet streams.
...@@ -474,6 +480,17 @@ public class ServerDialback { ...@@ -474,6 +480,17 @@ public class ServerDialback {
return null; return null;
} }
} }
/**
* Send a dialback error.
*
* @param from From
* @param to To
* @param err Error type.
*/
protected void dialbackError(String from, String to, PacketError err) {
connection.deliverRawText("<db:result type=\"error\" from=\"" + from + "\" to=\"" + to + "\">" + err.toXML() + "</db:result>");
}
/** /**
* Returns true if the domain requested by the remote server was validated by the Authoritative * Returns true if the domain requested by the remote server was validated by the Authoritative
...@@ -482,7 +499,7 @@ public class ServerDialback { ...@@ -482,7 +499,7 @@ public class ServerDialback {
* some other machine in the Originating Server's network.<p> * some other machine in the Originating Server's network.<p>
* *
* If the domain was not valid or some error occurred while validating the domain then the * If the domain was not valid or some error occurred while validating the domain then the
* underlying TCP connection will be closed. * underlying TCP connection may be closed.
* *
* @param doc the request for validating the new domain. * @param doc the request for validating the new domain.
* @param streamID the stream id generated by this server for the Originating Server. * @param streamID the stream id generated by this server for the Originating Server.
...@@ -495,7 +512,7 @@ public class ServerDialback { ...@@ -495,7 +512,7 @@ public class ServerDialback {
Log.debug("ServerDialback: RS - Received dialback key from host: " + hostname + " to: " + recipient); Log.debug("ServerDialback: RS - Received dialback key from host: " + hostname + " to: " + recipient);
if (!RemoteServerManager.canAccess(hostname)) { if (!RemoteServerManager.canAccess(hostname)) {
// Remote server is not allowed to establish a connection to this server // Remote server is not allowed to establish a connection to this server
connection.deliverRawText(new StreamError(StreamError.Condition.host_unknown).toXML()); connection.deliverRawText(new StreamError(StreamError.Condition.policy_violation).toXML());
// Close the underlying connection // Close the underlying connection
connection.close(); connection.close();
Log.debug("ServerDialback: RS - Error, hostname is not allowed to establish a connection to " + Log.debug("ServerDialback: RS - Error, hostname is not allowed to establish a connection to " +
...@@ -504,10 +521,7 @@ public class ServerDialback { ...@@ -504,10 +521,7 @@ public class ServerDialback {
return false; return false;
} }
else if (isHostUnknown(recipient)) { else if (isHostUnknown(recipient)) {
// address does not match a recognized hostname dialbackError(recipient, hostname, new PacketError(PacketError.Condition.item_not_found, PacketError.Type.cancel, "Service not hosted here"));
connection.deliverRawText(new StreamError(StreamError.Condition.host_unknown).toXML());
// Close the underlying connection
connection.close();
Log.debug("ServerDialback: RS - Error, hostname not recognized: " + recipient); Log.debug("ServerDialback: RS - Error, hostname not recognized: " + recipient);
return false; return false;
} }
...@@ -556,38 +570,47 @@ public class ServerDialback { ...@@ -556,38 +570,47 @@ public class ServerDialback {
} }
} }
if (!socket.isConnected()) { if (!socket.isConnected()) {
dialbackError(recipient, hostname, new PacketError(PacketError.Condition.remote_server_not_found, PacketError.Type.cancel, "Unable to connect to authoritative server"));
Log.warn("No server available for verifying key of remote server: " + hostname); Log.warn("No server available for verifying key of remote server: " + hostname);
try {
socket.close();
} catch (IOException e) {
Log.warn("Socket error on close", e);
}
return false; return false;
} }
try { try {
boolean valid = verifyKey(key, streamID.toString(), recipient, hostname, socket); VerifyResult result = verifyKey(key, streamID.toString(), recipient, hostname, socket);
Log.debug("ServerDialback: RS - Sending key verification result to OS: " + hostname); switch(result) {
sb = new StringBuilder(); case valid:
sb.append("<db:result"); case invalid:
sb.append(" from=\"").append(recipient).append("\""); boolean valid = (result == VerifyResult.valid);
sb.append(" to=\"").append(hostname).append("\""); Log.debug("ServerDialback: RS - Sending key verification result to OS: " + hostname);
sb.append(" type=\""); sb = new StringBuilder();
sb.append(valid ? "valid" : "invalid"); sb.append("<db:result");
sb.append("\"/>"); sb.append(" from=\"").append(recipient).append("\"");
connection.deliverRawText(sb.toString()); sb.append(" to=\"").append(hostname).append("\"");
sb.append(" type=\"");
if (!valid) { sb.append(valid ? "valid" : "invalid");
// Close the underlying connection sb.append("\"/>");
connection.close(); connection.deliverRawText(sb.toString());
if (!valid) {
// Close the underlying connection
connection.close();
}
return valid;
default:
break;
} }
return valid; dialbackError(recipient, hostname, new PacketError(PacketError.Condition.remote_server_timeout, PacketError.Type.cancel, "Authoritative server returned error"));
return false;
} }
catch (Exception e) { catch (Exception e) {
dialbackError(recipient, hostname, new PacketError(PacketError.Condition.remote_server_timeout, PacketError.Type.cancel, "Authoritative server failed"));
Log.warn("Error verifying key of remote server: " + hostname, e); Log.warn("Error verifying key of remote server: " + hostname, e);
// Send a <remote-connection-failed/> stream error condition
// and terminate both the XML stream and the underlying
// TCP connection
connection.deliverRawText(new StreamError(
StreamError.Condition.remote_connection_failed).toXML());
// Close the underlying connection
connection.close();
return false; return false;
} }
} }
...@@ -608,13 +631,14 @@ public class ServerDialback { ...@@ -608,13 +631,14 @@ public class ServerDialback {
/** /**
* Verifies the key with the Authoritative Server. * Verifies the key with the Authoritative Server.
*/ */
private boolean verifyKey(String key, String streamID, String recipient, String hostname, private VerifyResult verifyKey(String key, String streamID, String recipient, String hostname,
Socket socket) throws IOException, XmlPullParserException, Socket socket) throws IOException, XmlPullParserException,
RemoteConnectionFailedException { RemoteConnectionFailedException {
XMPPPacketReader reader; XMPPPacketReader reader;
Writer writer = null; Writer writer = null;
// Set a read timeout // Set a read timeout
socket.setSoTimeout(RemoteServerManager.getSocketTimeout()); socket.setSoTimeout(RemoteServerManager.getSocketTimeout());
VerifyResult result = VerifyResult.error;
try { try {
reader = new XMPPPacketReader(); reader = new XMPPPacketReader();
reader.setXPPFactory(FACTORY); reader.setXPPFactory(FACTORY);
...@@ -681,12 +705,17 @@ public class ServerDialback { ...@@ -681,12 +705,17 @@ public class ServerDialback {
// condition is sent to the Originating Server // condition is sent to the Originating Server
throw new RemoteConnectionFailedException("Invalid From"); throw new RemoteConnectionFailedException("Invalid From");
} }
else if ("valid".equals(doc.attributeValue("type"))){
Log.debug("ServerDialback: RS - Key was VERIFIED by the Authoritative Server for: {}", hostname);
result = VerifyResult.valid;
}
else if ("invalid".equals(doc.attributeValue("type"))){
Log.debug("ServerDialback: RS - Key was NOT VERIFIED by the Authoritative Server for: {}", hostname);
result = VerifyResult.invalid;
}
else { else {
boolean valid = "valid".equals(doc.attributeValue("type")); Log.debug("ServerDialback: RS - Key was ERRORED by the Authoritative Server for: {}", hostname);
Log.debug("ServerDialback: RS - Key was " + (valid ? "" : "NOT ") + result = VerifyResult.error;
"VERIFIED by the Authoritative Server for: " +
hostname);
return valid;
} }
} }
else { else {
...@@ -725,7 +754,7 @@ public class ServerDialback { ...@@ -725,7 +754,7 @@ public class ServerDialback {
// Do nothing // Do nothing
} }
} }
return false; return result;
} }
/** /**
......
...@@ -368,7 +368,7 @@ public class LocalIncomingServerSession extends LocalSession implements Incoming ...@@ -368,7 +368,7 @@ public class LocalIncomingServerSession extends LocalSession implements Incoming
} }
if (usingSelfSigned && ServerDialback.isEnabledForSelfSigned() && validatedDomains.isEmpty()) { if (usingSelfSigned && ServerDialback.isEnabledForSelfSigned() && validatedDomains.isEmpty()) {
sb.append("<dialback xmlns=\"urn:xmpp:features:dialback\"/>"); sb.append("<dialback xmlns=\"urn:xmpp:features:dialback\"><errors/></dialback>");
} }
return sb.toString(); return sb.toString();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment