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
...@@ -33,46 +35,64 @@ import java.nio.channels.WritableByteChannel; ...@@ -33,46 +35,64 @@ import java.nio.channels.WritableByteChannel;
*/ */
public class TLSStreamHandler { public class TLSStreamHandler {
private TLSStreamWriter writer; private TLSStreamWriter writer;
private TLSStreamReader reader; private TLSStreamReader reader;
private TLSWrapper wrapper; private TLSWrapper wrapper;
private ReadableByteChannel rbc; private ReadableByteChannel rbc;
private WritableByteChannel wbc; private WritableByteChannel wbc;
private SSLEngine tlsEngine; private SSLEngine tlsEngine;
/* /*
* During the initial handshake, keep track of the next SSLEngine operation that needs to occur: * During the initial handshake, keep track of the next SSLEngine operation that needs to occur:
* *
* NEED_WRAP/NEED_UNWRAP * NEED_WRAP/NEED_UNWRAP
* *
* Once the initial handshake has completed, we can short circuit handshake checks with * Once the initial handshake has completed, we can short circuit handshake checks with
* initialHSComplete. * initialHSComplete.
*/ */
private HandshakeStatus initialHSStatus; private HandshakeStatus initialHSStatus;
private boolean initialHSComplete; private boolean initialHSComplete;
private int appBBSize; private int appBBSize;
private int netBBSize; private int netBBSize;
/* /*
* All I/O goes through these buffers. It might be nice to use a cache of ByteBuffers so we're * All I/O goes through these buffers. It might be nice to use a cache of ByteBuffers so we're
* not alloc/dealloc'ing ByteBuffer's for each new SSLEngine. Outbound application data is * not alloc/dealloc'ing ByteBuffer's for each new SSLEngine. Outbound application data is
* supplied to us by our callers. * supplied to us by our callers.
*/ */
private ByteBuffer incomingNetBB; private ByteBuffer incomingNetBB;
private ByteBuffer outgoingNetBB; private ByteBuffer outgoingNetBB;
private ByteBuffer appBB; private ByteBuffer appBB;
/* /*
* An empty ByteBuffer for use when one isn't available, say as a source buffer during initial * An empty ByteBuffer for use when one isn't available, say as a source buffer during initial
* handshake wraps or for close operations. * handshake wraps or for close operations.
*/ */
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
...@@ -93,23 +113,23 @@ public class TLSStreamHandler { ...@@ -93,23 +113,23 @@ public class TLSStreamHandler {
boolean needClientAuth) throws IOException { boolean needClientAuth) throws IOException {
wrapper = new TLSWrapper(clientMode, needClientAuth, remoteServer); wrapper = new TLSWrapper(clientMode, needClientAuth, remoteServer);
tlsEngine = wrapper.getTlsEngine(); tlsEngine = wrapper.getTlsEngine();
reader = new TLSStreamReader(wrapper, socket); reader = new TLSStreamReader(wrapper, socket);
writer = new TLSStreamWriter(wrapper, socket); writer = new TLSStreamWriter(wrapper, socket);
rbc = Channels.newChannel(socket.getInputStream()); rbc = Channels.newChannel(socket.getInputStream());
wbc = Channels.newChannel(socket.getOutputStream()); wbc = Channels.newChannel(socket.getOutputStream());
initialHSStatus = HandshakeStatus.NEED_UNWRAP; initialHSStatus = HandshakeStatus.NEED_UNWRAP;
initialHSComplete = false; initialHSComplete = false;
netBBSize = tlsEngine.getSession().getPacketBufferSize(); netBBSize = tlsEngine.getSession().getPacketBufferSize();
appBBSize = tlsEngine.getSession().getApplicationBufferSize(); appBBSize = tlsEngine.getSession().getApplicationBufferSize();
incomingNetBB = ByteBuffer.allocate(netBBSize); incomingNetBB = ByteBuffer.allocate(netBBSize);
outgoingNetBB = ByteBuffer.allocate(netBBSize); outgoingNetBB = ByteBuffer.allocate(netBBSize);
outgoingNetBB.position(0); outgoingNetBB.position(0);
outgoingNetBB.limit(0); outgoingNetBB.limit(0);
appBB = ByteBuffer.allocate(appBBSize); appBB = ByteBuffer.allocate(appBBSize);
if (clientMode) { if (clientMode) {
socket.setSoTimeout(0); socket.setSoTimeout(0);
...@@ -122,175 +142,175 @@ public class TLSStreamHandler { ...@@ -122,175 +142,175 @@ public class TLSStreamHandler {
} }
while (!initialHSComplete) { while (!initialHSComplete) {
initialHSComplete = doHandshake(null); initialHSComplete = doHandshake(null);
} }
} }
public InputStream getInputStream(){ public InputStream getInputStream(){
return reader.getInputStream(); return reader.getInputStream();
} }
public OutputStream getOutputStream(){ public OutputStream getOutputStream(){
return writer.getOutputStream(); return writer.getOutputStream();
} }
private boolean doHandshake(SelectionKey sk) throws IOException { private boolean doHandshake(SelectionKey sk) throws IOException {
SSLEngineResult result; SSLEngineResult result;
if (initialHSComplete) { if (initialHSComplete) {
return initialHSComplete; return initialHSComplete;
} }
/* /*
* Flush out the outgoing buffer, if there's anything left in it. * Flush out the outgoing buffer, if there's anything left in it.
*/ */
if (outgoingNetBB.hasRemaining()) { if (outgoingNetBB.hasRemaining()) {
if (!flush(outgoingNetBB)) { if (!flush(outgoingNetBB)) {
return false; return false;
} }
// See if we need to switch from write to read mode. // See if we need to switch from write to read mode.
switch (initialHSStatus) { switch (initialHSStatus) {
/* /*
* Is this the last buffer? * Is this the last buffer?
*/ */
case FINISHED: case FINISHED:
initialHSComplete = true; initialHSComplete = true;
case NEED_UNWRAP: case NEED_UNWRAP:
if (sk != null) { if (sk != null) {
sk.interestOps(SelectionKey.OP_READ); sk.interestOps(SelectionKey.OP_READ);
} }
break; break;
} }
return initialHSComplete; return initialHSComplete;
} }
switch (initialHSStatus) { switch (initialHSStatus) {
case NEED_UNWRAP: case NEED_UNWRAP:
if (rbc.read(incomingNetBB) == -1) { if (rbc.read(incomingNetBB) == -1) {
tlsEngine.closeInbound(); tlsEngine.closeInbound();
return initialHSComplete; return initialHSComplete;
} }
needIO: while (initialHSStatus == HandshakeStatus.NEED_UNWRAP) { needIO: while (initialHSStatus == HandshakeStatus.NEED_UNWRAP) {
/* /*
* Don't need to resize requestBB, since no app data should be generated here. * Don't need to resize requestBB, since no app data should be generated here.
*/ */
incomingNetBB.flip(); incomingNetBB.flip();
result = tlsEngine.unwrap(incomingNetBB, appBB); result = tlsEngine.unwrap(incomingNetBB, appBB);
incomingNetBB.compact(); incomingNetBB.compact();
initialHSStatus = result.getHandshakeStatus(); initialHSStatus = result.getHandshakeStatus();
switch (result.getStatus()) { switch (result.getStatus()) {
case OK: case OK:
switch (initialHSStatus) { switch (initialHSStatus) {
case NOT_HANDSHAKING: case NOT_HANDSHAKING:
throw new IOException("Not handshaking during initial handshake"); throw new IOException("Not handshaking during initial handshake");
case NEED_TASK: case NEED_TASK:
initialHSStatus = doTasks(); initialHSStatus = doTasks();
break; break;
case FINISHED: case FINISHED:
initialHSComplete = true; initialHSComplete = true;
break needIO; break needIO;
} }
break; break;
case BUFFER_UNDERFLOW: case BUFFER_UNDERFLOW:
/* /*
* Need to go reread the Channel for more data. * Need to go reread the Channel for more data.
*/ */
if (sk != null) { if (sk != null) {
sk.interestOps(SelectionKey.OP_READ); sk.interestOps(SelectionKey.OP_READ);
} }
break needIO; break needIO;
default: // BUFFER_OVERFLOW/CLOSED: default: // BUFFER_OVERFLOW/CLOSED:
throw new IOException("Received" + result.getStatus() throw new IOException("Received" + result.getStatus()
+ "during initial handshaking"); + "during initial handshaking");
} }
} }
/* /*
* Just transitioned from read to write. * Just transitioned from read to write.
*/ */
if (initialHSStatus != HandshakeStatus.NEED_WRAP) { if (initialHSStatus != HandshakeStatus.NEED_WRAP) {
break; break;
} }
// Fall through and fill the write buffers. // Fall through and fill the write buffers.
case NEED_WRAP: case NEED_WRAP:
/* /*
* The flush above guarantees the out buffer to be empty * The flush above guarantees the out buffer to be empty
*/ */
outgoingNetBB.clear(); outgoingNetBB.clear();
result = tlsEngine.wrap(hsBB, outgoingNetBB); result = tlsEngine.wrap(hsBB, outgoingNetBB);
outgoingNetBB.flip(); outgoingNetBB.flip();
initialHSStatus = result.getHandshakeStatus(); initialHSStatus = result.getHandshakeStatus();
switch (result.getStatus()) { switch (result.getStatus()) {
case OK: case OK:
if (initialHSStatus == HandshakeStatus.NEED_TASK) { if (initialHSStatus == HandshakeStatus.NEED_TASK) {
initialHSStatus = doTasks(); initialHSStatus = doTasks();
} }
if (sk != null) { if (sk != null) {
sk.interestOps(SelectionKey.OP_WRITE); sk.interestOps(SelectionKey.OP_WRITE);
} }
break; break;
default: // BUFFER_OVERFLOW/BUFFER_UNDERFLOW/CLOSED: default: // BUFFER_OVERFLOW/BUFFER_UNDERFLOW/CLOSED:
throw new IOException("Received" + result.getStatus() throw new IOException("Received" + result.getStatus()
+ "during initial handshaking"); + "during initial handshaking");
} }
break; break;
default: // NOT_HANDSHAKING/NEED_TASK/FINISHED default: // NOT_HANDSHAKING/NEED_TASK/FINISHED
throw new RuntimeException("Invalid Handshaking State" + initialHSStatus); throw new RuntimeException("Invalid Handshaking State" + initialHSStatus);
} // switch } // switch
return initialHSComplete; return initialHSComplete;
} }
/* /*
* Writes ByteBuffer to the SocketChannel. Returns true when the ByteBuffer has no remaining * Writes ByteBuffer to the SocketChannel. Returns true when the ByteBuffer has no remaining
* data. * data.
*/ */
private boolean flush(ByteBuffer bb) throws IOException { private boolean flush(ByteBuffer bb) throws IOException {
wbc.write(bb); wbc.write(bb);
return !bb.hasRemaining(); return !bb.hasRemaining();
} }
/* /*
* Do all the outstanding handshake tasks in the current Thread. * Do all the outstanding handshake tasks in the current Thread.
*/ */
private SSLEngineResult.HandshakeStatus doTasks() { private SSLEngineResult.HandshakeStatus doTasks() {
Runnable runnable; Runnable runnable;
/* /*
* We could run this in a separate thread, but do in the current for now. * We could run this in a separate thread, but do in the current for now.
*/ */
while ((runnable = tlsEngine.getDelegatedTask()) != null) { while ((runnable = tlsEngine.getDelegatedTask()) != null) {
runnable.run(); runnable.run();
} }
return tlsEngine.getHandshakeStatus(); return tlsEngine.getHandshakeStatus();
} }
/** /**
* Closes the channels that will end up closing the input and output streams of the connection. * Closes the channels that will end up closing the input and output streams of the connection.
......
...@@ -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