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 {
}
}
protected void resetInput() {
public void resetInput() {
Reader oldReader = reader;
String oldEncoding = inputEncoding;
reset();
......
......@@ -286,7 +286,7 @@ public class SASLAuthentication {
// Check that hostname matches the one provided in a certificate
for (Certificate certificate : connection.getSSLSession().getPeerCertificates()) {
if (hostname
.equals(ServerTrustManager.getPeerIdentity((X509Certificate) certificate)))
.equals(TLSStreamHandler.getPeerIdentity((X509Certificate) certificate)))
{
authenticationSuccessful(hostname);
return true;
......
......@@ -79,7 +79,7 @@ class ServerTrustManager implements X509TrustManager {
if (verify) {
int nSize = x509Certificates.length;
String peerIdentity = getPeerIdentity(x509Certificates[0]);
String peerIdentity = TLSStreamHandler.getPeerIdentity(x509Certificates[0]);
if (JiveGlobals.getBooleanProperty("xmpp.server.certificate.verify.chain", true)) {
// Working down the chain, for every certificate in the chain,
......@@ -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) {
boolean trusted = false;
try {
......
......@@ -285,10 +285,6 @@ public abstract class SocketReader implements Runnable {
// resource binding and session establishment (to client sessions only)
compressionSuccessful();
}
else {
open = false;
session = null;
}
}
else
{
......@@ -354,8 +350,6 @@ public abstract class SocketReader implements Runnable {
if (error != null) {
// Deliver stanza
connection.deliverRawText(error);
// Close the underlying connection
connection.close();
return false;
}
else {
......
......@@ -24,6 +24,8 @@ import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectionKey;
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
......@@ -74,6 +76,24 @@ public class TLSStreamHandler {
*/
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
* to a remote server then <tt>clientMode</tt> will be <code>true</code> and
......
......@@ -148,6 +148,17 @@ public class IncomingServerSession extends Session {
openingStream.append(" version=\"1.0\">");
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();
sb.append("<stream:features>");
sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">");
......@@ -292,6 +303,11 @@ public class IncomingServerSession extends Session {
}
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
return null;
}
......
......@@ -18,6 +18,7 @@ import org.jivesoftware.wildfire.*;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.net.DNSUtil;
import org.jivesoftware.wildfire.net.SocketConnection;
import org.jivesoftware.wildfire.net.MXParser;
import org.jivesoftware.wildfire.spi.BasicStreamIDFactory;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
......@@ -38,6 +39,9 @@ import java.util.Collections;
import java.util.Iterator;
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
* connection is used for sending packets while the other connection is used for receiving packets.
......@@ -298,7 +302,7 @@ public class OutgoingServerSession extends Session {
// Secure the connection with TLS and authenticate using SASL
OutgoingServerSession answer;
answer = secureAndAuthenticate(hostname, connection, reader, openingStream,
xpp, domain);
domain);
if (answer != null) {
// Everything went fine so return the secured and
// authenticated connection
......@@ -334,11 +338,12 @@ public class OutgoingServerSession extends Session {
private static OutgoingServerSession secureAndAuthenticate(String hostname,
SocketConnection connection, XMPPPacketReader reader, StringBuilder openingStream,
XmlPullParser xpp, String domain) throws Exception {
String domain) throws Exception {
Element features;
Log.debug("OS - Indicating we want TLS to " + hostname);
connection.deliverRawText("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
MXParser xpp = (MXParser) reader.getXPPParser();
// Wait for the <proceed> response
Element proceed = reader.parseDocument().getRootElement();
if (proceed != null && proceed.getName().equals("proceed")) {
......@@ -359,6 +364,66 @@ public class OutgoingServerSession extends Session {
// Get new stream features
features = reader.parseDocument().getRootElement();
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();
while (it.hasNext()) {
Element mechanism = (Element) it.next();
......@@ -369,9 +434,8 @@ public class OutgoingServerSession extends Session {
// SASL was successful so initiate a new stream
connection.deliverRawText(openingStream.toString());
// Reset the parser to use the new secured reader
xpp.setInput(new InputStreamReader(
connection.getTLSStreamHandler().getInputStream(), CHARSET));
// Reset the parser
xpp.resetInput();
// Skip the opening stream sent by the server
for (int eventType = xpp.getEventType();
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