Commit 5399d0b0 authored by Gaston Dombiak's avatar Gaston Dombiak Committed by gato

1) Added compression for s2s.

2) Connection is no longer being closed when compression fails.

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@3278 b35dd754-fafc-0310-a699-88a17e54d16e
parent 15412cb3
...@@ -325,7 +325,7 @@ public class MXParser extends org.xmlpull.mxp1.MXParser { ...@@ -325,7 +325,7 @@ public class MXParser extends org.xmlpull.mxp1.MXParser {
} }
} }
protected void resetInput() { public void resetInput() {
Reader oldReader = reader; Reader oldReader = reader;
String oldEncoding = inputEncoding; String oldEncoding = inputEncoding;
reset(); reset();
......
...@@ -286,7 +286,7 @@ public class SASLAuthentication { ...@@ -286,7 +286,7 @@ public class SASLAuthentication {
// Check that hostname matches the one provided in a certificate // Check that hostname matches the one provided in a certificate
for (Certificate certificate : connection.getSSLSession().getPeerCertificates()) { for (Certificate certificate : connection.getSSLSession().getPeerCertificates()) {
if (hostname if (hostname
.equals(ServerTrustManager.getPeerIdentity((X509Certificate) certificate))) .equals(TLSStreamHandler.getPeerIdentity((X509Certificate) certificate)))
{ {
authenticationSuccessful(hostname); authenticationSuccessful(hostname);
return true; return true;
......
...@@ -79,7 +79,7 @@ class ServerTrustManager implements X509TrustManager { ...@@ -79,7 +79,7 @@ class ServerTrustManager implements X509TrustManager {
if (verify) { if (verify) {
int nSize = x509Certificates.length; int nSize = x509Certificates.length;
String peerIdentity = getPeerIdentity(x509Certificates[0]); String peerIdentity = TLSStreamHandler.getPeerIdentity(x509Certificates[0]);
if (JiveGlobals.getBooleanProperty("xmpp.server.certificate.verify.chain", true)) { if (JiveGlobals.getBooleanProperty("xmpp.server.certificate.verify.chain", true)) {
// Working down the chain, for every certificate in the chain, // Working down the chain, for every certificate in the chain,
...@@ -164,24 +164,6 @@ class ServerTrustManager implements X509TrustManager { ...@@ -164,24 +164,6 @@ class ServerTrustManager implements X509TrustManager {
} }
} }
/**
* Returns the identity of the remote server as defined in the specified certificate. The
* identity is defined in the subjectDN of the certificate and it can also be defined in
* the subjectAltName extension of type "xmpp". When the extension is being used then the
* identity defined in the extension in going to be returned. Otherwise, the value stored in
* the subjectDN is returned.
*
* @param x509Certificate the certificate the holds the identity of the remote server.
* @return the identity of the remote server as defined in the specified certificate.
*/
static String getPeerIdentity(X509Certificate x509Certificate) {
Principal principalSubject = x509Certificate.getSubjectDN();
// TODO Look the identity in the subjectAltName extension if available
String name = principalSubject.getName();
name = name.replace("CN=", "");
return name;
}
private boolean isChainTrusted(X509Certificate[] chain) { private boolean isChainTrusted(X509Certificate[] chain) {
boolean trusted = false; boolean trusted = false;
try { try {
......
...@@ -285,10 +285,6 @@ public abstract class SocketReader implements Runnable { ...@@ -285,10 +285,6 @@ public abstract class SocketReader implements Runnable {
// resource binding and session establishment (to client sessions only) // resource binding and session establishment (to client sessions only)
compressionSuccessful(); compressionSuccessful();
} }
else {
open = false;
session = null;
}
} }
else else
{ {
...@@ -354,8 +350,6 @@ public abstract class SocketReader implements Runnable { ...@@ -354,8 +350,6 @@ public abstract class SocketReader implements Runnable {
if (error != null) { if (error != null) {
// Deliver stanza // Deliver stanza
connection.deliverRawText(error); connection.deliverRawText(error);
// Close the underlying connection
connection.close();
return false; return false;
} }
else { else {
......
...@@ -24,6 +24,8 @@ import java.nio.channels.Channels; ...@@ -24,6 +24,8 @@ import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectionKey; import java.nio.channels.SelectionKey;
import java.nio.channels.WritableByteChannel; import java.nio.channels.WritableByteChannel;
import java.security.cert.X509Certificate;
import java.security.Principal;
/** /**
* TLSStreamHandler is responsible for securing plain connections by negotiating TLS. By creating * TLSStreamHandler is responsible for securing plain connections by negotiating TLS. By creating
...@@ -74,6 +76,24 @@ public class TLSStreamHandler { ...@@ -74,6 +76,24 @@ public class TLSStreamHandler {
*/ */
private static ByteBuffer hsBB = ByteBuffer.allocate(0); private static ByteBuffer hsBB = ByteBuffer.allocate(0);
/**
* Returns the identity of the remote server as defined in the specified certificate. The
* identity is defined in the subjectDN of the certificate and it can also be defined in
* the subjectAltName extension of type "xmpp". When the extension is being used then the
* identity defined in the extension in going to be returned. Otherwise, the value stored in
* the subjectDN is returned.
*
* @param x509Certificate the certificate the holds the identity of the remote server.
* @return the identity of the remote server as defined in the specified certificate.
*/
public static String getPeerIdentity(X509Certificate x509Certificate) {
Principal principalSubject = x509Certificate.getSubjectDN();
// TODO Look the identity in the subjectAltName extension if available
String name = principalSubject.getName();
name = name.replace("CN=", "");
return name;
}
/** /**
* Creates a new TLSStreamHandler and secures the plain socket connection. When connecting * Creates a new TLSStreamHandler and secures the plain socket connection. When connecting
* to a remote server then <tt>clientMode</tt> will be <code>true</code> and * to a remote server then <tt>clientMode</tt> will be <code>true</code> and
......
...@@ -148,6 +148,17 @@ public class IncomingServerSession extends Session { ...@@ -148,6 +148,17 @@ public class IncomingServerSession extends Session {
openingStream.append(" version=\"1.0\">"); openingStream.append(" version=\"1.0\">");
connection.deliverRawText(openingStream.toString()); connection.deliverRawText(openingStream.toString());
// Indicate the TLS policy to use for this connection
connection.setTlsPolicy(ServerDialback.isEnabled() ? Connection.TLSPolicy.optional :
Connection.TLSPolicy.required);
// Indicate the compression policy to use for this connection
String policyName = JiveGlobals.getProperty("xmpp.server.compression.policy",
Connection.CompressionPolicy.disabled.toString());
Connection.CompressionPolicy compressionPolicy =
Connection.CompressionPolicy.valueOf(policyName);
connection.setCompressionPolicy(compressionPolicy);
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("<stream:features>"); sb.append("<stream:features>");
sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">"); sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">");
...@@ -292,6 +303,11 @@ public class IncomingServerSession extends Session { ...@@ -292,6 +303,11 @@ public class IncomingServerSession extends Session {
} }
public String getAvailableStreamFeatures() { public String getAvailableStreamFeatures() {
// Include Stream Compression Mechanism
if (conn.getCompressionPolicy() != Connection.CompressionPolicy.disabled &&
!conn.isCompressed()) {
return "<compression xmlns=\"http://jabber.org/features/compress\"><method>zlib</method></compression>";
}
// Nothing special to add // Nothing special to add
return null; return null;
} }
......
...@@ -18,6 +18,7 @@ import org.jivesoftware.wildfire.*; ...@@ -18,6 +18,7 @@ import org.jivesoftware.wildfire.*;
import org.jivesoftware.wildfire.auth.UnauthorizedException; import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.net.DNSUtil; import org.jivesoftware.wildfire.net.DNSUtil;
import org.jivesoftware.wildfire.net.SocketConnection; import org.jivesoftware.wildfire.net.SocketConnection;
import org.jivesoftware.wildfire.net.MXParser;
import org.jivesoftware.wildfire.spi.BasicStreamIDFactory; import org.jivesoftware.wildfire.spi.BasicStreamIDFactory;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils; import org.jivesoftware.util.LocaleUtils;
...@@ -38,6 +39,9 @@ import java.util.Collections; ...@@ -38,6 +39,9 @@ import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import com.jcraft.jzlib.ZInputStream;
import com.jcraft.jzlib.JZlib;
/** /**
* Server-to-server communication is done using two TCP connections between the servers. One * Server-to-server communication is done using two TCP connections between the servers. One
* connection is used for sending packets while the other connection is used for receiving packets. * connection is used for sending packets while the other connection is used for receiving packets.
...@@ -298,7 +302,7 @@ public class OutgoingServerSession extends Session { ...@@ -298,7 +302,7 @@ public class OutgoingServerSession extends Session {
// Secure the connection with TLS and authenticate using SASL // Secure the connection with TLS and authenticate using SASL
OutgoingServerSession answer; OutgoingServerSession answer;
answer = secureAndAuthenticate(hostname, connection, reader, openingStream, answer = secureAndAuthenticate(hostname, connection, reader, openingStream,
xpp, domain); domain);
if (answer != null) { if (answer != null) {
// Everything went fine so return the secured and // Everything went fine so return the secured and
// authenticated connection // authenticated connection
...@@ -334,11 +338,12 @@ public class OutgoingServerSession extends Session { ...@@ -334,11 +338,12 @@ public class OutgoingServerSession extends Session {
private static OutgoingServerSession secureAndAuthenticate(String hostname, private static OutgoingServerSession secureAndAuthenticate(String hostname,
SocketConnection connection, XMPPPacketReader reader, StringBuilder openingStream, SocketConnection connection, XMPPPacketReader reader, StringBuilder openingStream,
XmlPullParser xpp, String domain) throws Exception { String domain) throws Exception {
Element features; Element features;
Log.debug("OS - Indicating we want TLS to " + hostname); Log.debug("OS - Indicating we want TLS to " + hostname);
connection.deliverRawText("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"); connection.deliverRawText("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
MXParser xpp = (MXParser) reader.getXPPParser();
// Wait for the <proceed> response // Wait for the <proceed> response
Element proceed = reader.parseDocument().getRootElement(); Element proceed = reader.parseDocument().getRootElement();
if (proceed != null && proceed.getName().equals("proceed")) { if (proceed != null && proceed.getName().equals("proceed")) {
...@@ -359,6 +364,66 @@ public class OutgoingServerSession extends Session { ...@@ -359,6 +364,66 @@ public class OutgoingServerSession extends Session {
// Get new stream features // Get new stream features
features = reader.parseDocument().getRootElement(); features = reader.parseDocument().getRootElement();
if (features != null && features.element("mechanisms") != null) { if (features != null && features.element("mechanisms") != null) {
// Check if we can use stream compression
String policyName = JiveGlobals.getProperty("xmpp.server.compression.policy",
Connection.CompressionPolicy.disabled.toString());
Connection.CompressionPolicy compressionPolicy =
Connection.CompressionPolicy.valueOf(policyName);
if (Connection.CompressionPolicy.optional == compressionPolicy) {
// Verify if the remote server supports stream compression
Element compression = features.element("compression");
if (compression != null) {
boolean zlibSupported = false;
Iterator it = compression.elementIterator("method");
while (it.hasNext()) {
Element method = (Element) it.next();
if ("zlib".equals(method.getTextTrim())) {
zlibSupported = true;
}
}
if (zlibSupported) {
// Request Stream Compression
connection.deliverRawText("<compress xmlns='http://jabber.org/protocol/compress'><method>zlib</method></compress>");
// Check if we are good to start compression
Element answer = reader.parseDocument().getRootElement();
if ("compressed".equals(answer.getName())) {
// Server confirmed that we can use zlib compression
connection.startCompression();
Log.debug("OS - Stream compression was successful with " + hostname);
// Stream compression was successful so initiate a new stream
connection.deliverRawText(openingStream.toString());
// Reset the parser to use stream compression over TLS
ZInputStream in = new ZInputStream(
connection.getTLSStreamHandler().getInputStream());
in.setFlushMode(JZlib.Z_PARTIAL_FLUSH);
xpp.setInput(new InputStreamReader(in, CHARSET));
// Skip the opening stream sent by the server
for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG;)
{
eventType = xpp.next();
}
// Get new stream features
features = reader.parseDocument().getRootElement();
if (features == null || features.element("mechanisms") == null) {
Log.debug("OS - Error, EXTERNAL SASL was not offered by " + hostname);
return null;
}
}
else {
Log.debug("OS - Stream compression was rejected by " + hostname);
}
}
else {
Log.debug(
"OS - Stream compression found but zlib method is not supported by" +
hostname);
}
}
else {
Log.debug("OS - Stream compression not supoprted by " + hostname);
}
}
Iterator it = features.element("mechanisms").elementIterator(); Iterator it = features.element("mechanisms").elementIterator();
while (it.hasNext()) { while (it.hasNext()) {
Element mechanism = (Element) it.next(); Element mechanism = (Element) it.next();
...@@ -369,9 +434,8 @@ public class OutgoingServerSession extends Session { ...@@ -369,9 +434,8 @@ public class OutgoingServerSession extends Session {
// SASL was successful so initiate a new stream // SASL was successful so initiate a new stream
connection.deliverRawText(openingStream.toString()); connection.deliverRawText(openingStream.toString());
// Reset the parser to use the new secured reader // Reset the parser
xpp.setInput(new InputStreamReader( xpp.resetInput();
connection.getTLSStreamHandler().getInputStream(), CHARSET));
// Skip the opening stream sent by the server // Skip the opening stream sent by the server
for (int eventType = xpp.getEventType(); for (int eventType = xpp.getEventType();
eventType != XmlPullParser.START_TAG;) { eventType != XmlPullParser.START_TAG;) {
......
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